// Copyright 2020-2021 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package queries

import (
	"fmt"
	"math"
	"time"

	"github.com/dolthub/vitess/go/mysql"
	"github.com/dolthub/vitess/go/sqltypes"
	"github.com/dolthub/vitess/go/vt/sqlparser"
	"gopkg.in/src-d/go-errors.v1"

	"github.com/dolthub/go-mysql-server/sql"
	"github.com/dolthub/go-mysql-server/sql/analyzer/analyzererrors"
	"github.com/dolthub/go-mysql-server/sql/plan"
	"github.com/dolthub/go-mysql-server/sql/planbuilder"
	"github.com/dolthub/go-mysql-server/sql/types"
)

type ScriptTest struct {
	// Name of the script test
	Name string
	// The sql statements to execute as setup, in order. Results are not checked, but statements must not error.
	SetUpScript []string
	// The set of assertions to make after setup, in order
	Assertions []ScriptTestAssertion
	// For tests that make a single assertion, Query can be set for the single assertion
	Query string
	// For tests that make a single assertion, Expected can be set for the single assertion
	Expected []sql.Row
	// For tests that make a single assertion, ExpectedErr can be set for the expected error
	ExpectedErr *errors.Kind
	// For tests that make a single assertion, ExpectedIndexes can be set for the string representation of indexes that we expect to appear in the query plan
	ExpectedIndexes []string
	// For tests that perform join operations, JoinTypes can be set for the type of merge we expect to perform.
	JoinTypes []plan.JoinType
	// SkipPrepared is true when we skip a test for prepared statements only
	SkipPrepared bool
	// Dialect is the supported dialect for this script, which must match the dialect of the harness if specified.
	// The script is skipped if the dialect doesn't match.
	Dialect string
	// Skip is used to completely skip a test, not execute any part of the script, and to record it as a skipped test in
	// the test suite results.
	Skip bool
}

type ScriptTestAssertion struct {
	Query       string
	Expected    []sql.Row
	ExpectedErr *errors.Kind
	// ExpectedErrStr should be set for tests that expect a specific error string this is not linked to a custom error.
	// In most cases, errors should be linked to a custom error, however there are exceptions where this is not possible,
	// such as the use of the SIGNAL statement.
	ExpectedErrStr string

	// ExpectedWarning contains the expected warning code when a query generates warnings but not errors.
	ExpectedWarning int

	// ExpectedWarningsCount is used to test the expected number of warnings generated by a query.
	// The ExpectedWarning field must be set for warning counts to be checked.
	ExpectedWarningsCount int

	// ExpectedWarningMessageSubstring is used to test the contents of warning messages generated by a
	// query. The ExpectedWarning field must be set for warning messages to be checked.
	ExpectedWarningMessageSubstring string

	// ExpectedColumns indicates the Name and Type of the columns expected; no other schema fields are tested.
	ExpectedColumns sql.Schema

	// The string representation of indexes that we expect to appear in the query plan
	ExpectedIndexes []string

	// For tests that perform join operations, JoinTypes can be set for the type of merge we expect to perform.
	JoinTypes []plan.JoinType

	// For tests that expect a specific plan
	ExpectedPlan string

	// NewSession instructs the test framework that this assertion requires a new session to be created before the
	// query is executed. This is generally only needed when a test script is testing functionality that invalidates
	// a session and prevents additional queries from being executed on the session.
	NewSession bool

	// SkipResultsCheck is used to skip assertions on expected Rows returned from a query. This should be used
	// sparingly, such as in cases where you only want to test warning messages.
	SkipResultsCheck bool

	// Skip is used to completely skip a test, not execute its query at all, and record it as a skipped test
	// in the test suite results.
	Skip bool

	// SkipResultCheckOnServerEngine is used when the result of over the wire test does not match the result from the engine test.
	// It should be fixed in the future.
	SkipResultCheckOnServerEngine bool

	// Bindings are variable mappings only used for prepared tests
	Bindings map[string]sqlparser.Expr

	// CheckIndexedAccess indicates whether we should verify the query plan uses an index
	CheckIndexedAccess bool

	// Dialect is the supported dialect for this assertion, which must match the dialect of the harness if specified.
	// The assertion is skipped if the dialect doesn't match.
	Dialect string
}

// ScriptTests are a set of test scripts to run.
// Unlike other engine tests, ScriptTests must be self-contained. No other tables are created outside the definition of
// the tests.
var ScriptTests = []ScriptTest{
	{
		// https://github.com/dolthub/dolt/issues/10113
		Name: "DELETE with NOT EXISTS subquery",
		SetUpScript: []string{
			`CREATE TABLE IF NOT EXISTS student (
				id BIGINT AUTO_INCREMENT,
				name VARCHAR(50) NOT NULL,
				PRIMARY KEY (id)
			);`,
			`CREATE TABLE IF NOT EXISTS student_hobby (
				id BIGINT AUTO_INCREMENT,
				student_id BIGINT NOT NULL,
				hobby VARCHAR(50) NOT NULL,
				PRIMARY KEY (id)
			);`,
			"INSERT INTO student (id, name) VALUES (1, 'test1');",
			"INSERT INTO student (id, name) VALUES (2, 'test2');",
			"INSERT INTO student_hobby (id, student_id, hobby) VALUES (1, 1, 'test1');",
			"INSERT INTO student_hobby (id, student_id, hobby) VALUES (2, 2, 'test2');",
			"INSERT INTO student_hobby (id, student_id, hobby) VALUES (3, 100, 'test3');",
			"INSERT INTO student_hobby (id, student_id, hobby) VALUES (4, 100, 'test3');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "delete from student_hobby where not exists (select 1 from student where student.id = student_hobby.student_id);",
			},
			{
				Query:    "SELECT * FROM student_hobby ORDER BY id;",
				Expected: []sql.Row{{1, 1, "test1"}, {2, 2, "test2"}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9987
		Name: "GROUP BY nil pointer dereference in Dispose when Next() never called",
		SetUpScript: []string{
			"CREATE TABLE test_table (id INT PRIMARY KEY, value INT, category VARCHAR(50))",
			"INSERT INTO test_table VALUES (1, 100, 'A'), (2, 200, 'B'), (3, 300, 'A')",
		},
		Assertions: []ScriptTestAssertion{
			{
				// LIMIT 0 causes the iterator to close without ever calling Next() on groupByIter
				// This leaves all buffer elements as nil causing panic in Dispose(), or empty depending data struct
				Query:    "SELECT category, SUM(value) FROM test_table GROUP BY category LIMIT 0",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT category, COUNT(*) FROM test_table GROUP BY category LIMIT 0",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT SUM(value) FROM test_table LIMIT 0",
				Expected: []sql.Row{},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9935
		Dialect: "mysql",
		Name:    "Incorrect use of negation in AntiJoinIncludingNulls",
		SetUpScript: []string{
			"CREATE TABLE t0(c0 INT);",
			"INSERT INTO t0(c0) VALUES(1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT * FROM t0 WHERE (! (1 || (EXISTS (SELECT 1))));",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! (0 || (EXISTS (SELECT 1))));",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! ((EXISTS (SELECT 1)) || 0));",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! ((EXISTS (SELECT 1)) || 1));",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! (1 && (EXISTS (SELECT 1))));",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! (0 && (EXISTS (SELECT 1))));",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! (0 || (EXISTS (SELECT 1 FROM t0 WHERE c0 = 2))));",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! (1 || (EXISTS (SELECT 1 FROM t0 WHERE c0 = 1))));",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t0 WHERE (! (0 || (EXISTS (SELECT 1 FROM t0 WHERE c0 = 1))));",
				Expected: []sql.Row{},
			},
		},
	},
	{
		// https://github.com/dolthub/go-mysql-server/issues/3259
		Dialect: "mysql",
		Name:    "Missing column with same name as system variable",
		SetUpScript: []string{
			"CREATE DATABASE IF NOT EXISTS test_db",
			"USE test_db",
			"CREATE TABLE A (id INT)",
			"CREATE TABLE B (id INT)",
			"INSERT INTO A VALUES (1)",
			"INSERT INTO B VALUES (2)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "SELECT UNIX_TIMESTAMP(A.timestamp) FROM A LIMIT 1",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT A.timestamp FROM A",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT A.version FROM A",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT A.max_connections FROM A",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT UPPER(A.sql_mode) FROM A",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:            "SELECT @@timestamp",
				Expected:         []sql.Row{{float64(0)}},
				SkipResultsCheck: true, // dynamic var
			},
			{
				Query:            "SELECT @@version",
				Expected:         []sql.Row{{""}},
				SkipResultsCheck: true, // dynamic var
			},
			{
				Query:       "SELECT test_db.A.timestamp FROM A",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT test_db.A.version FROM test_db.A",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT a1.timestamp FROM A a1 JOIN B b1 ON a1.id = b1.id",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:       "SELECT b1.max_connections FROM A a1 JOIN B b1 ON a1.id = b1.id",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9927
		// https://github.com/dolthub/dolt/issues/9053
		Name:    "double negation of integer minimum values",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t0(c0 BIGINT);",
			"INSERT INTO t0(c0) VALUES (-9223372036854775808);",
			"CREATE TABLE t1(c0 INT);",
			"INSERT INTO t1(c0) VALUES (-2147483648);",
			"CREATE TABLE t2(c0 SMALLINT);",
			"INSERT INTO t2(c0) VALUES (-32768);",
			"CREATE TABLE t3(c0 TINYINT);",
			"INSERT INTO t3(c0) VALUES (-128);",
			"CREATE TABLE tab1 (col4 INT)",
			"INSERT INTO tab1 VALUES (10), (20), (30)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:           "SELECT -(-128);",
				Expected:        []sql.Row{{int16(128)}},
				ExpectedColumns: sql.Schema{{Name: "-(-128)", Type: types.Int64}},
			},
			{
				Query:           "SELECT -(-32768);",
				Expected:        []sql.Row{{int32(32768)}},
				ExpectedColumns: sql.Schema{{Name: "-(-32768)", Type: types.Int64}},
			},
			{
				Query:           "SELECT -(-2147483648);",
				Expected:        []sql.Row{{int64(2147483648)}},
				ExpectedColumns: sql.Schema{{Name: "-(-2147483648)", Type: types.Int64}},
			},
			{
				Query:           "SELECT -(-9223372036854775808)",
				Expected:        []sql.Row{{"9223372036854775808"}},
				ExpectedColumns: sql.Schema{{Name: "-(-9223372036854775808)", Type: types.InternalDecimalType}},
			},
			{
				Query:       "SELECT -t0.c0 FROM t0;",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query:    "SELECT -t1.c0 FROM t1;",
				Expected: []sql.Row{{2147483648}},
			},
			{
				Query:    "SELECT -t2.c0 FROM t2;",
				Expected: []sql.Row{{32768}},
			},
			{
				Query:    "SELECT -t3.c0 FROM t3;",
				Expected: []sql.Row{{128}},
			},
			{
				Query:    "SELECT -(-t1.c0 + 1) FROM t1;",
				Expected: []sql.Row{{-2147483649}},
			},
			{
				Query:    "SELECT -(-(t2.c0 - 1)) FROM t2;",
				Expected: []sql.Row{{-32769}},
			},
			{
				Query:    "SELECT -(-t3.c0 * 2) FROM t3;",
				Expected: []sql.Row{{-256}},
			},
			{
				Query:    "SELECT -(-(-128));",
				Expected: []sql.Row{{int8(-128)}},
			},
			{
				Query:    "SELECT -(-(-(-128)));",
				Expected: []sql.Row{{int16(128)}},
			},
			{
				Query:    "SELECT -(-NULL);",
				Expected: []sql.Row{{nil}},
			},
			{
				Query:           "SELECT -(-CAST(-128 AS SIGNED));",
				Expected:        []sql.Row{{int64(-128)}},
				ExpectedColumns: sql.Schema{{Name: "-(-CAST(-128 AS SIGNED))", Type: types.Int64}},
			},
			{
				Query:    "SELECT * FROM tab1 AS cor0 WHERE NOT - CAST(NULL AS SIGNED) < +35 * +col4 + - -39",
				Expected: []sql.Row{},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9865
		Name:    "Stored procedure containing a transaction does not return EOF",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test_table (id INT PRIMARY KEY, name TEXT)",
			`CREATE PROCEDURE my_proc()
BEGIN
    START TRANSACTION;
    INSERT INTO test_table VALUES (1, 'test');
    COMMIT;
END`,
			`CREATE PROCEDURE empty_procedure()
BEGIN
END`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "CALL my_proc()",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0, InsertID: 0, Info: nil}}},
			},
			{
				Query:    "SELECT * FROM test_table",
				Expected: []sql.Row{{1, "test"}},
			},
			{
				Query:    "CALL empty_procedure()",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0, InsertID: 0, Info: nil}}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9873
		// TODO: `FOR UPDATE OF` (`FOR UPDATE` in general) is currently a no-op: https://www.dolthub.com/blog/2023-10-23-hold-my-beer/
		Name:    "FOR UPDATE OF syntax support tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE task_instance (id INT PRIMARY KEY, task_id VARCHAR(255), dag_id VARCHAR(255), run_id VARCHAR(255), state VARCHAR(50), queued_by_job_id INT)",
			"CREATE TABLE job (id INT PRIMARY KEY, state VARCHAR(50))",
			"CREATE TABLE dag_run (dag_id VARCHAR(255), run_id VARCHAR(255), state VARCHAR(50))",
			"CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(50))",
			"INSERT INTO task_instance VALUES (1, 'task1', 'dag1', 'run1', 'running', 1)",
			"INSERT INTO job VALUES (1, 'running')",
			"INSERT INTO dag_run VALUES ('dag1', 'run1', 'running')",
			"INSERT INTO t VALUES (1, 'test')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `SELECT task_instance.id, task_instance.task_id, task_instance.dag_id, task_instance.run_id
FROM task_instance INNER JOIN job ON job.id = task_instance.queued_by_job_id INNER JOIN dag_run ON dag_run.dag_id = task_instance.dag_id AND dag_run.run_id = task_instance.run_id
 WHERE task_instance.state IN ('running', 'queued', 'scheduled') AND NOT (job.state <=> 'running') AND dag_run.state = 'running' FOR UPDATE OF task_instance SKIP LOCKED`,
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT * FROM t FOR UPDATE",
				Expected: []sql.Row{{1, "test"}},
			},
			{
				Query:    "SELECT * FROM t FOR UPDATE OF t",
				Expected: []sql.Row{{1, "test"}},
			},
			{
				Query:    "SELECT * FROM t FOR UPDATE OF t SKIP LOCKED",
				Expected: []sql.Row{{1, "test"}},
			},
			{
				Query:    "SELECT * FROM t FOR UPDATE OF t NOWAIT",
				Expected: []sql.Row{{1, "test"}},
			},
			{
				Query:    "SELECT * FROM task_instance t1, job t2 FOR UPDATE OF t1, t2",
				Expected: []sql.Row{{1, "task1", "dag1", "run1", "running", 1, 1, "running"}},
			},
			{
				Query:       "SELECT * FROM t FOR UPDATE OF nonexistent_table",
				ExpectedErr: sql.ErrUnresolvedTableLock,
			},
			{
				Query:          "SELECT * FROM t FOR UPDATE OF t, nonexistent_table",
				ExpectedErr:    sql.ErrUnresolvedTableLock,
				ExpectedErrStr: fmt.Sprintf(sql.ErrUnresolvedTableLock.Message, "nonexistent_table"),
			},
			{
				Query:       "SELECT * FROM t FOR UPDATE OF",
				ExpectedErr: sql.ErrSyntaxError,
			},
			{
				Query:       "SELECT * FROM t FOR UPDATE test",
				ExpectedErr: sql.ErrSyntaxError,
			},
			{
				Query:    "SELECT * FROM t FOR UPDATE",
				Expected: []sql.Row{{1, "test"}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9872
		Name:        "TEXT(m) syntax support",
		SetUpScript: []string{},
		Dialect:     "mysql",
		Assertions: []ScriptTestAssertion{
			{
				Query: "CREATE TABLE task_instance_note (ti_id VARCHAR(36) NOT NULL, user_id VARCHAR(128), content TEXT(1000), created_at TIMESTAMP(6) NOT NULL, updated_at TIMESTAMP(6) NOT NULL, CONSTRAINT task_instance_note_pkey PRIMARY KEY (ti_id))",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "DESCRIBE task_instance_note",
				Expected: []sql.Row{
					{"ti_id", "varchar(36)", "NO", "PRI", nil, ""},
					{"user_id", "varchar(128)", "YES", "", nil, ""},
					{"content", "text", "YES", "", nil, ""},
					{"created_at", "timestamp(6)", "NO", "", nil, ""},
					{"updated_at", "timestamp(6)", "NO", "", nil, ""},
				},
			},
			{
				Query: "CREATE TABLE tiny (t TEXT(255))",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "DESCRIBE tiny",
				Expected: []sql.Row{
					{"t", "tinytext", "YES", "", nil, ""},
				},
			},
			{
				Query: "CREATE TABLE smallt (s TEXT(65535))",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "DESCRIBE smallt",
				Expected: []sql.Row{
					{"s", "text", "YES", "", nil, ""},
				},
			},
			{
				Query: "CREATE TABLE mediumt (m TEXT(16777215))",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "DESCRIBE mediumt",
				Expected: []sql.Row{
					{"m", "mediumtext", "YES", "", nil, ""},
				},
			},
			{
				Query: "CREATE TABLE longt (l TEXT(4294967295))",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "DESCRIBE longt",
				Expected: []sql.Row{
					{"l", "longtext", "YES", "", nil, ""},
				},
			},
			{
				Query: "CREATE TABLE d (t TEXT)",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "DESCRIBE d",
				Expected: []sql.Row{
					{"t", "text", "YES", "", nil, ""},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9857
		Name:        "UUID_SHORT() function returns 64-bit unsigned integers with proper construction",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT UUID_SHORT() > 0",
				Expected: []sql.Row{
					{true}, // Should return positive values
				},
			},
			{
				Query: "SELECT UUID_SHORT() != UUID_SHORT()",
				Expected: []sql.Row{
					{true}, // Should return different values on each call
				},
			},
			{
				Query: "SELECT UUID_SHORT() + 0 > 0",
				Expected: []sql.Row{
					{true}, // Should work in arithmetic expressions
				},
			},
			{
				Query: "SELECT CAST(UUID_SHORT() AS CHAR) != ''",
				Expected: []sql.Row{
					{true}, // Should cast to non-empty string
				},
			},
			{
				Query: "SELECT UUID_SHORT() BETWEEN 1 AND 18446744073709551615",
				Expected: []sql.Row{
					{true}, // Should be within uint64 range
				},
			},
			{
				Query: "SELECT (UUID_SHORT() & 0xFF00000000000000) >> 56 BETWEEN 0 AND 255",
				Expected: []sql.Row{
					{true}, // Server ID should be 0-255
				},
			},
			{
				Query: "SET @@global.server_id = 253",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "SELECT (UUID_SHORT() & 0xFF00000000000000) >> 56 BETWEEN 0 AND 255",
				Expected: []sql.Row{
					{true}, // server time won't let us pin this down further
				},
			},
			{
				Query: "SET @@global.server_id = 1",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/go-mysql-server/issues/3216
		Name: "UNION ALL with BLOB columns",
		SetUpScript: []string{
			"CREATE TABLE a(name VARCHAR(255), data BLOB)",
			"CREATE TABLE b(name VARCHAR(255), data BLOB)",
			"INSERT INTO a VALUES ('a-data', UNHEX('deadbeef'))",
			"INSERT INTO b VALUES ('b-nodata', NULL)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT name, data FROM a UNION ALL SELECT name, data FROM b",
				Expected: []sql.Row{
					{"a-data", []byte{0xde, 0xad, 0xbe, 0xef}},
					{"b-nodata", nil},
				},
			},
			{
				Query: "SELECT name, HEX(data) as data_hex FROM a UNION ALL SELECT name, HEX(data) as data_hex FROM b",
				Expected: []sql.Row{
					{"a-data", "DEADBEEF"},
					{"b-nodata", nil},
				},
			},
			{
				Query: "SELECT name, data FROM a UNION ALL SELECT name, NULL FROM b",
				Expected: []sql.Row{
					{"a-data", []byte{0xde, 0xad, 0xbe, 0xef}},
					{"b-nodata", nil},
				},
			},
			{
				Query: "SELECT name, HEX(data) as data_hex FROM a UNION ALL SELECT name, HEX(NULL) as data_hex FROM b",
				Expected: []sql.Row{
					{"a-data", "DEADBEEF"},
					{"b-nodata", nil},
				},
			},
			{
				Query: "SELECT name, data FROM a UNION ALL SELECT name, UNHEX('') FROM b",
				Expected: []sql.Row{
					{"a-data", []byte{0xde, 0xad, 0xbe, 0xef}},
					{"b-nodata", []byte{}},
				},
			},
			{
				Query: "SELECT name, HEX(data) as data_hex FROM a UNION ALL SELECT name, HEX(UNHEX('')) as data_hex FROM b",
				Expected: []sql.Row{
					{"a-data", "DEADBEEF"},
					{"b-nodata", ""},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9836
		Skip: true,
		Name: "Ordering by pk does not change the order of results",
		SetUpScript: []string{
			"CREATE TABLE test(pk VARCHAR(50) PRIMARY KEY)",
			"INSERT INTO test VALUES ('  3 12 4'), ('3. 12 4'), ('3.2 12 4'), ('-3.1234'), ('-3.1a'), ('-5+8'), ('+3.1234')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT pk FROM test ORDER BY pk",
				Expected: []sql.Row{{"  3 12 4"}, {"-3.1234"}, {"-3.1a"}, {"-5+8"}, {"+3.1234"}, {"3. 12 4"}, {"3.2 12 4"}},
			},
		},
	},
	{
		Dialect: "mysql",
		Name:    "string to number comparison correctly truncates",
		Assertions: []ScriptTestAssertion{
			{
				Query:                           "SELECT 'A' = 0;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:                           "SELECT 'A' != 0;",
				Expected:                        []sql.Row{{false}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:                           "SELECT 'A' <> 0;",
				Expected:                        []sql.Row{{false}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:                           "SELECT 'A' < 0;",
				Expected:                        []sql.Row{{false}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:                           "SELECT 'A' <= 0;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:                           "SELECT 'A' > 0;",
				Expected:                        []sql.Row{{false}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:                           "SELECT 'A' >= 0;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:    "SELECT '' = 0;",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select 'abc' = false",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select '1abc' = true",
				Expected: []sql.Row{{true}},
			},
			{
				// Truncates to 123
				Query:    "select '123abc' = false",
				Expected: []sql.Row{{false}},
			},
			{
				// Truncates to 123
				Query:    "select '123abc' = true",
				Expected: []sql.Row{{false}},
			},
			{
				Query:                           "SELECT '123A' = 123;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 123A",
			},
			{
				Query:                           "SELECT 'A123' = 0;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A123",
			},
			{
				Query:    "SELECT '123.456' = 123;",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "SELECT '123.456' = 123.456;",
				Expected: []sql.Row{{true}},
			},
			{
				// TODO: 123.456 is converted to a DECIMAL by Builder.ConvertVal, when it should be a DOUBLE
				SkipResultCheckOnServerEngine:   true, // TODO: warnings do not make it to server engine
				Query:                           "SELECT '123.456ABC' = 123.456;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect decimal(65,30) value: 123.456ABC",
			},
			{
				Query:    "SELECT '123.456e2' = 12345.6;",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "SELECT '123.456e-2' = 1.23456;",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "SELECT '1.9a' = 1.9;",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "SELECT 1 where '1.9a' = 1.9;",
				Expected: []sql.Row{{1}},
			},
			{
				// Valid float strings used as arguments to functions are truncated not rounded
				Query:                 "SELECT LENGTH(SPACE('1.9'));",
				Expected:              []sql.Row{{1}},
				ExpectedWarningsCount: 1, // TODO: MySQL throws two warnings for some reason
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				// TODO: 123.456 is converted to a DECIMAL by Builder.ConvertVal, when it should be a DOUBLE
				Query:                           "SELECT -'+123.456ABC' = -123.456",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect decimal(65,30) value: +123.456ABC",
			},
			{
				Query:                           "SELECT '0xBEEF' = 0;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 0xBEEF",
			},
			{
				// 'A' is truncated to 0
				Query:                           "SELECT 'A' IN (0)",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				// 'A' is truncated to 0
				Query:                           "SELECT 'A' NOT IN (0)",
				Expected:                        []sql.Row{{false}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A",
			},
			{
				Query:    "SELECT '' in (0);",
				Expected: []sql.Row{{true}},
			},
			{
				Query:                           "SELECT '123A' in (123);",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 123A",
			},
			{
				Query:                           "SELECT 123 in ('123A');",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 123A",
			},
			{
				Query:                           "SELECT 'A123' in (0);",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: A123",
			},
			{
				Query:                           "SELECT '123abc' in ('string', 1, 2, 123);",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           3, // MySQL only throws 1 warning
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value",
			},
			{
				Query:                           "SELECT 123 in ('string', 1, 2, '123abc');",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           2,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value",
			},
			{
				Query:                           "SELECT '123A' in (123);",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 123A",
			},
			{
				Query:    "SELECT '123.456' in (123);",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "SELECT '123.456' in (123.456);",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "SELECT 123.456 in (123.456);",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "SELECT 123.45 in (123.4);",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "SELECT 123.45 in (123.5);",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "SELECT '123.45a' in (123.5);",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "SELECT '123.45a' in (123.4);",
				Expected: []sql.Row{{false}},
			},
			{
				SkipResultCheckOnServerEngine:   true, // TODO: warnings do not make it to server engine
				Query:                           "SELECT '123.456ABC' in (123.456);",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect decimal(65,30) value: 123.456ABC",
			},
			{
				Query:    "SELECT '123.456e2' in (12345.6);",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "SELECT '123.456e-2' in (1.23456);",
				Expected: []sql.Row{{true}},
			},
			{
				Query:                           "SELECT '0xBEEF' = 0;",
				Expected:                        []sql.Row{{true}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 0xBEEF",
			},
			{
				Query:                           `select 'a' + 4;`,
				Expected:                        []sql.Row{{4.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           `select '20a' + 4;`,
				Expected:                        []sql.Row{{24.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '20a'",
			},
			{
				Query:                           `select '10.a' + 4;`,
				Expected:                        []sql.Row{{14.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '10.a'",
			},
			{
				Query:                           `select '.20a' + 4;`,
				Expected:                        []sql.Row{{4.2}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '.20a'",
			},
			{
				Query:                           `select 4 + 'a';`,
				Expected:                        []sql.Row{{4.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                 `select 'a' + 'a';`,
				Expected:              []sql.Row{{0.0}},
				ExpectedWarningsCount: 2,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Query:                           `select 'a' - 4;`,
				Expected:                        []sql.Row{{-4.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           `select 4 - 'a';`,
				Expected:                        []sql.Row{{4.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           `select 4 - '2a';`,
				Expected:                        []sql.Row{{2.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '2a'",
			},
			{
				Query:                 `select 'a' - 'a';`,
				Expected:              []sql.Row{{0.0}},
				ExpectedWarningsCount: 2,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Query:                           `select 'a' * 4;`,
				Expected:                        []sql.Row{{0.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           `select 4 * 'a';`,
				Expected:                        []sql.Row{{0.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                 `select 'a' * 'a';`,
				Expected:              []sql.Row{{0.0}},
				ExpectedWarningsCount: 2,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Query:                           "select 1 * '2.50a';",
				Expected:                        []sql.Row{{2.5}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '2.50a'",
			},
			{
				Query:                           "select 1 * '2.a50a';",
				Expected:                        []sql.Row{{2.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '2.a50a'",
			},
			{
				Query:                           `select 'a' / 4;`,
				Expected:                        []sql.Row{{0.0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                 `select 4 / 'a';`,
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:                 `select 'a' / 'a';`,
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:                 "select 1 / '2.50a';",
				Expected:              []sql.Row{{0.4}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:                 "select 1 / '2.a50a';",
				Expected:              []sql.Row{{0.5}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:                           "select 'a' & 'a';",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           2,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' & 4;",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 4 & 'a';",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' | 'a';",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           2,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' | 4;",
				Expected:                        []sql.Row{{uint64(4)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' | -1;",
				Expected:                        []sql.Row{{uint64(18446744073709551615)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 4 | 'a';",
				Expected:                        []sql.Row{{uint64(4)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' ^ 'a';",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           2,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' ^ 4;",
				Expected:                        []sql.Row{{uint64(4)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' ^ -1;",
				Expected:                        []sql.Row{{uint64(18446744073709551615)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 4 ^ 'a';",
				Expected:                        []sql.Row{{uint64(4)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' >> 'a';",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           2,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' >> 4;",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 4 >> 'a';",
				Expected:                        []sql.Row{{uint64(4)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select -1 >> 'a';",
				Expected:                        []sql.Row{{uint64(18446744073709551615)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' << 'a';",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           2,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select 'a' << 4;",
				Expected:                        []sql.Row{{uint64(0)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select '2a' << 4;",
				Expected:                        []sql.Row{{uint64(32)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '2a'",
			},
			{
				Query:                           "select 4 << 'a';",
				Expected:                        []sql.Row{{uint64(4)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                           "select -1 << 'a';",
				Expected:                        []sql.Row{{uint64(18446744073709551615)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                 "select 'a' div 'a';",
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 3, // Truncated incorrect value (2c) and Divide by Zero
			},
			{
				Query:                           "select 'a' div 4;",
				Expected:                        []sql.Row{{0}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                 "select 4 div 'a';",
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:    "select 1.2 div '1';",
				Expected: []sql.Row{{1}},
			},
			{
				Query:                 "select 1.2 div 'a1';",
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:                           "select '12a' div '3' ;",
				Expected:                        []sql.Row{{4}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: '12a'",
			},
			{
				Query:                 "select 'a' mod 'a';",
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:                           "select 'a' mod 4;",
				Expected:                        []sql.Row{{float64(0)}},
				ExpectedWarningsCount:           1,
				ExpectedWarning:                 mysql.ERTruncatedWrongValue,
				ExpectedWarningMessageSubstring: "Truncated incorrect double value: 'a'",
			},
			{
				Query:                 "select 4 mod 'a';",
				Expected:              []sql.Row{{nil}},
				ExpectedWarningsCount: 2, // Truncated incorrect value and Divide by Zero
			},
			{
				Query:    "SELECT '127' | '128', '128' << 2;",
				Expected: []sql.Row{{uint64(255), uint64(512)}},
			},
			{
				Query:    "SELECT X'7F' | X'80', X'80' << 2;",
				Expected: []sql.Row{{uint64(255), uint64(512)}},
			},
			{
				Query:    "SELECT X'40' | X'01', b'11110001' & b'01001111';",
				Expected: []sql.Row{{uint64(65), uint64(65)}},
			},
			{
				Skip:     true,
				Query:    "SELECT 0x1 = 1;",
				Expected: []sql.Row{{true}},
			},
		},
	},
	{
		// Related Issues:
		//   https://github.com/dolthub/dolt/issues/9733
		//   https://github.com/dolthub/dolt/issues/9739
		Dialect: "mysql",
		Name:    "strings cast to numbers",
		SetUpScript: []string{
			"create table test01(pk varchar(20) primary key);",
			`insert into test01 values ('  3 12 4'),
                          ('  3.2 12 4'),('-3.1234'),('-3.1a'),('-5+8'),('+3.1234'),
                          ('11d'),('11wha?'),('11'),('12'),('1a1'),('a1a1'),('11-5'),
                          ('3. 12 4'),('5.932887e+07'),('5.932887e+07abc'),('5.932887e7'),('5.932887e7abc');`,
			"create table test02(pk int primary key);",
			"insert into test02 values(11),(12),(13),(14),(15);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select pk, cast(pk as float) from test01",
				Expected: []sql.Row{
					{"  3 12 4", float32(3)},
					{"  3.2 12 4", float32(3.2)},
					{"-3.1234", float32(-3.1234)},
					{"-3.1a", float32(-3.1)},
					{"-5+8", float32(-5)},
					{"+3.1234", float32(3.1234)},
					{"11", float32(11)},
					{"11-5", float32(11)},
					{"11d", float32(11)},
					{"11wha?", float32(11)},
					{"12", float32(12)},
					{"1a1", float32(1)},
					{"3. 12 4", float32(3)},
					{"5.932887e+07", float32(5.932887e+07)},
					{"5.932887e+07abc", float32(5.932887e+07)},
					{"5.932887e7", float32(5.932887e+07)},
					{"5.932887e7abc", float32(5.932887e+07)},
					{"a1a1", float32(0)},
				},
				ExpectedWarningsCount: 12,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Dialect: "mysql",
				Query:   "select pk, cast(pk as double) from test01",
				Expected: []sql.Row{
					{"  3 12 4", 3.0},
					{"  3.2 12 4", 3.2},
					{"-3.1234", -3.1234},
					{"-3.1a", -3.1},
					{"-5+8", -5.0},
					{"+3.1234", 3.1234},
					{"11", 11.0},
					{"11-5", 11.0},
					{"11d", 11.0},
					{"11wha?", 11.0},
					{"12", 12.0},
					{"1a1", 1.0},
					{"3. 12 4", 3.0},
					{"5.932887e+07", 5.932887e+07},
					{"5.932887e+07abc", 5.932887e+07},
					{"5.932887e7", 5.932887e+07},
					{"5.932887e7abc", 5.932887e+07},
					{"a1a1", 0.0},
				},
				ExpectedWarningsCount: 12,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Query: "select pk, cast(pk as signed) from test01",
				Expected: []sql.Row{
					{"  3 12 4", 3},
					{"  3.2 12 4", 3},
					{"-3.1234", -3},
					{"-3.1a", -3},
					{"-5+8", -5},
					{"+3.1234", 3},
					{"11", 11},
					{"11-5", 11},
					{"11d", 11},
					{"11wha?", 11},
					{"12", 12},
					{"1a1", 1},
					{"3. 12 4", 3},
					{"5.932887e+07", 5},
					{"5.932887e+07abc", 5},
					{"5.932887e7", 5},
					{"5.932887e7abc", 5},
					{"a1a1", 0},
				},
				ExpectedWarningsCount: 16,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Query: "select pk, cast(pk as unsigned) from test01",
				Expected: []sql.Row{
					{"  3 12 4", uint64(3)},
					{"  3.2 12 4", uint64(3)},
					{"-3.1234", uint64(18446744073709551613)},
					{"-3.1a", uint64(18446744073709551613)},
					{"-5+8", uint64(18446744073709551611)},
					{"+3.1234", uint64(3)},
					{"11", uint64(11)},
					{"11-5", uint64(11)},
					{"11d", uint64(11)},
					{"11wha?", uint64(11)},
					{"12", uint64(12)},
					{"1a1", uint64(1)},
					{"3. 12 4", uint64(3)},
					{"5.932887e+07", uint64(5)},
					{"5.932887e+07abc", uint64(5)},
					{"5.932887e7", uint64(5)},
					{"5.932887e7abc", uint64(5)},
					{"a1a1", uint64(0)},
				},
				ExpectedWarningsCount: 19,
				// Can't check multiple different warnings
			},
			{
				Query: "select pk, cast(pk as decimal(12,3)) from test01",
				Expected: []sql.Row{
					{"  3 12 4", "3.000"},
					{"  3.2 12 4", "3.200"},
					{"-3.1234", "-3.123"},
					{"-3.1a", "-3.100"},
					{"-5+8", "-5.000"},
					{"+3.1234", "3.123"},
					{"11", "11.000"},
					{"11-5", "11.000"},
					{"11d", "11.000"},
					{"11wha?", "11.000"},
					{"12", "12.000"},
					{"1a1", "1.000"},
					{"3. 12 4", "3.000"},
					{"5.932887e+07", "59328870.000"},
					{"5.932887e+07abc", "59328870.000"},
					{"5.932887e7", "59328870.000"},
					{"5.932887e7abc", "59328870.000"},
					{"a1a1", "0.000"},
				},
				// TODO: should be 13. Missing warning for "Incorrect DECIMAL value: '0' for column '' at row -1" (1366)
				ExpectedWarningsCount: 12,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Query:                 "select * from test01 where pk in ('11')",
				Expected:              []sql.Row{{"11"}},
				ExpectedWarningsCount: 0,
			},
			{
				// https://github.com/dolthub/dolt/issues/9739
				Dialect: "mysql",
				Query:   "select * from test01 where pk in (11)",
				Expected: []sql.Row{
					{"11"},
					{"11-5"},
					{"11d"},
					{"11wha?"},
				},
				ExpectedWarningsCount: 12,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				// https://github.com/dolthub/dolt/issues/9739
				Skip:    true, // this passes in gms but not dolt
				Dialect: "mysql",
				Query:   "select * from test01 where pk=3",
				Expected: []sql.Row{
					{"  3 12 4"},
					{"3. 12 4"},
				},
				ExpectedWarningsCount: 12,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				// https://github.com/dolthub/dolt/issues/9739
				Dialect: "mysql",
				Query:   "select * from test01 where pk>=3 and pk < 4",
				Expected: []sql.Row{
					{"  3 12 4"},
					{"  3.2 12 4"},
					{"+3.1234"},
					{"3. 12 4"},
				},
				ExpectedWarningsCount: 20,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Dialect:               "mysql",
				Query:                 "select * from test02 where pk in ('11asdf');",
				Expected:              []sql.Row{{11}},
				ExpectedWarningsCount: 1,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
			{
				Dialect:               "mysql",
				Query:                 "select * from test02 where pk='11.12asdf';",
				Expected:              []sql.Row{},
				ExpectedWarningsCount: 1,
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9936
		Name:    "invisible hash index with different key types",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t0(c0 varchar(500))",
			"insert into t0(c0) values (77367106)",
			"create index i0 using hash on t0(c0) invisible",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select c0 from t0 where 1630944823 >= t0.c0",
				Expected: []sql.Row{{"77367106"}},
			},
			{
				Query:    "select c0 from t0 where 1630944823 <= t0.c0",
				Expected: []sql.Row{},
			},
			{
				Query:    "select c0 from t0 where '1630944823' >= t0.c0",
				Expected: []sql.Row{},
			},
			{
				Query:    "select c0 from t0 where '1630944823' <= t0.c0",
				Expected: []sql.Row{{"77367106"}},
			},
			{
				Query:    "select c0 from t0 where 1630944823.2 >= t0.c0",
				Expected: []sql.Row{{"77367106"}},
			},
			{
				Query:    "select c0 from t0 where 1630944823.2 <= t0.c0",
				Expected: []sql.Row{},
			},
			{
				Query:    "select c0 from t0 where '1630944823.2' >= t0.c0",
				Expected: []sql.Row{},
			},
			{
				Query:    "select c0 from t0 where '1630944823.2' <= t0.c0",
				Expected: []sql.Row{{"77367106"}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9821
		Name: "strings with boolean operators",
		Assertions: []ScriptTestAssertion{
			{
				Dialect:               "mysql",
				Query:                 `select '3bxu' and true`,
				Expected:              []sql.Row{{true}},
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
				ExpectedWarningsCount: 1,
			},
			{
				Dialect:               "mysql",
				Query:                 "select '3bxu' or false",
				Expected:              []sql.Row{{true}},
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
				ExpectedWarningsCount: 1,
			},
			{
				Dialect:               "mysql",
				Query:                 "select '3bxu' xor false",
				Expected:              []sql.Row{{true}},
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
				ExpectedWarningsCount: 1,
			},
			{
				Query:                 "select '' or false",
				Expected:              []sql.Row{{false}},
				ExpectedWarningsCount: 0,
			},
			{
				Query:                 "select '0' or false",
				Expected:              []sql.Row{{false}},
				ExpectedWarningsCount: 0,
			},
			{
				Query:                 "select '00' or false",
				Expected:              []sql.Row{{false}},
				ExpectedWarningsCount: 0,
			},
			{
				Dialect:               "mysql",
				Query:                 "select '00asdf' or false",
				Expected:              []sql.Row{{false}},
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
				ExpectedWarningsCount: 1,
			},
			{
				Dialect:               "mysql",
				Query:                 "select 'asdf' or false",
				Expected:              []sql.Row{{false}},
				ExpectedWarning:       mysql.ERTruncatedWrongValue,
				ExpectedWarningsCount: 1,
			},
		},
	},
	{
		Dialect: "mysql",
		Name:    "complicated string to numeric conversion",
		SetUpScript: []string{
			"CREATE TABLE t0(c INT);",
			"INSERT INTO t0 VALUES (1);",
			"CREATE TABLE t1(c VARCHAR(500));",
			"INSERT INTO t1 VALUES ('1a');",
			"CREATE TABLE t2(c0 INT , c1 BOOLEAN , c2 BOOLEAN , c3 INT , placeholder0 INT , placeholder1 VARCHAR(500) , placeholder2 VARCHAR(500) , PRIMARY KEY(placeholder0));",
			"CREATE TABLE t3(c0 INT , c1 VARCHAR(500) , c2 BOOLEAN , c3 VARCHAR(500) , placeholder0 BOOLEAN , placeholder1 INT , placeholder2 VARCHAR(500));",
			"INSERT INTO t3 VALUES (7, '0y4', TRUE, '5y', TRUE, 5, 'p9c');",
			"INSERT INTO t3 VALUES (1, '4', TRUE, '4H', FALSE, 9, 'Zy4');",
			"INSERT INTO t3 VALUES (10, '1a', FALSE, 'pYE', FALSE, 3, '0awX');",
			"INSERT INTO t3 VALUES (8, 'J', TRUE, 'LE', TRUE, 9, 'YEqQ');",
			"INSERT INTO t2 VALUES (10, FALSE, TRUE, 2, 2, 'nfxF', 'xvC');",
			"INSERT INTO t2 VALUES (10, TRUE, TRUE, 10, 1, 'rlQT', 'W');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT * FROM t0, t1 WHERE (t1.c IN (true));",
				Expected: []sql.Row{
					{1, "1a"},
				},
			},
			{
				Query: "SELECT * FROM t3 INNER JOIN t2 ON ((((t3.c0) = ((EXTRACT(YEAR FROM DATE_ADD(DATE '2000-01-01', INTERVAL ( BIT_LENGTH(( MOD(t2.c3 + ( t2.c3 + ( BIT_COUNT(t2.c3) ) * 3 - CAST(( NOT (t2.c0 XOR t2.c2) ) AS SIGNED) ) * 2, 100 + t2.c3) ) ^ t2.c3) ) DAY)) % (t2.c3 + 1))))) >= (((t3.c2) < ((((((('Bs./')OR('wZ')) IN ((('1066274936')OR('')))))OR((((t3.c1 IN (true)))<>(((t3.c0)OR(( COALESCE(NULLIF(t3.c3, ''), t3.c1) ))))))))))));",
				Expected: []sql.Row{
					{7, "0y4", 1, "5y", 1, 5, "p9c", 10, 1, 1, 10, 1, "rlQT", "W"},
					{1, "4", 1, "4H", 0, 9, "Zy4", 10, 1, 1, 10, 1, "rlQT", "W"},
					{10, "1a", 0, "pYE", 0, 3, "0awX", 10, 1, 1, 10, 1, "rlQT", "W"},
					{8, "J", 1, "LE", 1, 9, "YEqQ", 10, 1, 1, 10, 1, "rlQT", "W"},
					{7, "0y4", 1, "5y", 1, 5, "p9c", 10, 0, 1, 2, 2, "nfxF", "xvC"},
					{1, "4", 1, "4H", 0, 9, "Zy4", 10, 0, 1, 2, 2, "nfxF", "xvC"},
					{10, "1a", 0, "pYE", 0, 3, "0awX", 10, 0, 1, 2, 2, "nfxF", "xvC"},
					{8, "J", 1, "LE", 1, 9, "YEqQ", 10, 0, 1, 2, 2, "nfxF", "xvC"},
				},
			},
			{
				Query: "SELECT * FROM t3 CROSS JOIN t2 WHERE ((((t3.c0) = ((EXTRACT(YEAR FROM DATE_ADD(DATE '2000-01-01', INTERVAL ( BIT_LENGTH(( MOD(t2.c3 + ( t2.c3 + ( BIT_COUNT(t2.c3) ) * 3 - CAST(( NOT (t2.c0 XOR t2.c2) ) AS SIGNED) ) * 2, 100 + t2.c3) ) ^ t2.c3) ) DAY)) % (t2.c3 + 1))))) >= (((t3.c2) < ((((((('Bs./')OR('wZ')) IN ((('1066274936')OR('')))))OR((((t3.c1 IN (true)))<>(((t3.c0)OR(( COALESCE(NULLIF(t3.c3, ''), t3.c1) ))))))))))));",
				Expected: []sql.Row{
					{7, "0y4", 1, "5y", 1, 5, "p9c", 10, 1, 1, 10, 1, "rlQT", "W"},
					{1, "4", 1, "4H", 0, 9, "Zy4", 10, 1, 1, 10, 1, "rlQT", "W"},
					{10, "1a", 0, "pYE", 0, 3, "0awX", 10, 1, 1, 10, 1, "rlQT", "W"},
					{8, "J", 1, "LE", 1, 9, "YEqQ", 10, 1, 1, 10, 1, "rlQT", "W"},
					{7, "0y4", 1, "5y", 1, 5, "p9c", 10, 0, 1, 2, 2, "nfxF", "xvC"},
					{1, "4", 1, "4H", 0, 9, "Zy4", 10, 0, 1, 2, 2, "nfxF", "xvC"},
					{10, "1a", 0, "pYE", 0, 3, "0awX", 10, 0, 1, 2, 2, "nfxF", "xvC"},
					{8, "J", 1, "LE", 1, 9, "YEqQ", 10, 0, 1, 2, 2, "nfxF", "xvC"},
				},
			},
		},
	},

	{
		// https://github.com/dolthub/dolt/issues/9794
		Name: "UPDATE with TRIM function on TEXT column",
		SetUpScript: []string{
			"create table my_table (txt text);",
			"insert into my_table values('foobar');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:            "update my_table set txt = trim(txt);",
				SkipResultsCheck: true,
			},
			{
				Query:    "select txt from my_table;",
				Expected: []sql.Row{{"foobar"}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9794
		Name:    "String functions with TextStorage (comprehensive test)",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table test_strings (id int primary key, content text);",
			"insert into test_strings values (1, '  Hello World  '), (2, 'Test String'), (3, 'LOWERCASE'), (4, 'abc123def');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select id, trim(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "Hello World"}, {2, "Test String"}, {3, "LOWERCASE"}, {4, "abc123def"}},
			},
			{
				Query:    "select id, upper(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "  HELLO WORLD  "}, {2, "TEST STRING"}, {3, "LOWERCASE"}, {4, "ABC123DEF"}},
			},
			{
				Query:    "select id, lower(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "  hello world  "}, {2, "test string"}, {3, "lowercase"}, {4, "abc123def"}},
			},
			{
				Query:    "select id, reverse(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "  dlroW olleH  "}, {2, "gnirtS tseT"}, {3, "ESACREWOL"}, {4, "fed321cba"}},
			},
			{
				Query:    "select id, substring(content, 1, 5) from test_strings order by id;",
				Expected: []sql.Row{{1, "  Hel"}, {2, "Test "}, {3, "LOWER"}, {4, "abc12"}},
			},
			{
				Query:    "select id, length(content) from test_strings order by id;",
				Expected: []sql.Row{{1, 15}, {2, 11}, {3, 9}, {4, 9}},
			},
			{
				Query:    "select id, left(content, 3) from test_strings order by id;",
				Expected: []sql.Row{{1, "  H"}, {2, "Tes"}, {3, "LOW"}, {4, "abc"}},
			},
			{
				Query:    "select id, right(content, 3) from test_strings order by id;",
				Expected: []sql.Row{{1, "d  "}, {2, "ing"}, {3, "ASE"}, {4, "def"}},
			},
			{
				Query:    "select id, ltrim(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "Hello World  "}, {2, "Test String"}, {3, "LOWERCASE"}, {4, "abc123def"}},
			},
			{
				Query:    "select id, rtrim(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "  Hello World"}, {2, "Test String"}, {3, "LOWERCASE"}, {4, "abc123def"}},
			},
			{
				Query:    "select id, replace(content, 'e', 'X') from test_strings order by id;",
				Expected: []sql.Row{{1, "  HXllo World  "}, {2, "TXst String"}, {3, "LOWERCASE"}, {4, "abc123dXf"}},
			},
			{
				Query:    "select id, repeat(substring(content, 1, 2), 2) from test_strings order by id;",
				Expected: []sql.Row{{1, "    "}, {2, "TeTe"}, {3, "LOLO"}, {4, "abab"}},
			},
			{
				Query:    "select id, lpad(content, 12, '*') from test_strings where id = 4;",
				Expected: []sql.Row{{4, "***abc123def"}},
			},
			{
				Query:    "select id, rpad(content, 12, '*') from test_strings where id = 4;",
				Expected: []sql.Row{{4, "abc123def***"}},
			},
			{
				Query:    "select id, locate('o', content) from test_strings order by id;",
				Expected: []sql.Row{{1, 7}, {2, 0}, {3, 2}, {4, 0}},
			},
			{
				Query:    "select id, position('o' in content) from test_strings order by id;",
				Expected: []sql.Row{{1, 7}, {2, 0}, {3, 2}, {4, 0}},
			},
			{
				Query:    "select id, substr(content, 2, 4) from test_strings order by id;",
				Expected: []sql.Row{{1, " Hel"}, {2, "est "}, {3, "OWER"}, {4, "bc12"}},
			},
			{
				Query:    "select id, mid(content, 3, 3) from test_strings order by id;",
				Expected: []sql.Row{{1, "Hel"}, {2, "st "}, {3, "WER"}, {4, "c12"}},
			},
			{
				Query:    "select id, char_length(content) from test_strings order by id;",
				Expected: []sql.Row{{1, 15}, {2, 11}, {3, 9}, {4, 9}},
			},
			{
				Query:    "select id, character_length(content) from test_strings order by id;",
				Expected: []sql.Row{{1, 15}, {2, 11}, {3, 9}, {4, 9}},
			},
			{
				Query:    "select id, octet_length(content) from test_strings order by id;",
				Expected: []sql.Row{{1, 15}, {2, 11}, {3, 9}, {4, 9}},
			},
			{
				Query:    "select id, lcase(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "  hello world  "}, {2, "test string"}, {3, "lowercase"}, {4, "abc123def"}},
			},
			{
				Query:    "select id, ucase(content) from test_strings order by id;",
				Expected: []sql.Row{{1, "  HELLO WORLD  "}, {2, "TEST STRING"}, {3, "LOWERCASE"}, {4, "ABC123DEF"}},
			},
			{
				Query:    "select id, ascii(content) from test_strings order by id;",
				Expected: []sql.Row{{1, uint64(32)}, {2, uint64(84)}, {3, uint64(76)}, {4, uint64(97)}},
			},
			{
				Query:    "select id, hex(content) from test_strings where id = 4;",
				Expected: []sql.Row{{4, "616263313233646566"}},
			},
			{
				Query:    "select id, unhex(hex(content)) = content from test_strings where id = 4;",
				Expected: []sql.Row{{4, true}},
			},
			{
				Query:    "select id, substring_index(content, 'e', 1) from test_strings order by id;",
				Expected: []sql.Row{{1, "  H"}, {2, "T"}, {3, "LOWERCASE"}, {4, "abc123d"}},
			},
			{
				Query:    "select id, insert(content, 2, 3, 'XYZ') from test_strings where id = 4;",
				Expected: []sql.Row{{4, "aXYZ23def"}},
			},
			{
				Query:            "update test_strings set content = concat(trim(content), '!');",
				SkipResultsCheck: true,
			},
			{
				Query:    "select id, content from test_strings order by id;",
				Expected: []sql.Row{{1, "Hello World!"}, {2, "Test String!"}, {3, "LOWERCASE!"}, {4, "abc123def!"}},
			},
			{
				Query:    "SELECT CONCAT_WS(',', content, 'suffix') FROM test_strings WHERE id = 2;",
				Expected: []sql.Row{{"Test String!,suffix"}},
			},
			{
				Query:    "SELECT EXPORT_SET(5, content, 'off') FROM test_strings WHERE id = 2;",
				Expected: []sql.Row{{"Test String!,off,Test String!,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off,off"}},
			},
			{
				Query:    "SELECT FIND_IN_SET('String', content) FROM test_strings WHERE id = 2;",
				Expected: []sql.Row{{int32(0)}},
			},
			{
				Query:    "SELECT MAKE_SET(3, content, 'second', 'third') FROM test_strings WHERE id = 2;",
				Expected: []sql.Row{{"Test String!,second"}},
			},
			{
				Query:    "SELECT SOUNDEX(content) FROM test_strings WHERE id = 2;",
				Expected: []sql.Row{{"T2323652"}},
			},
		},
	},
	{
		// Regression test for https://github.com/dolthub/dolt/issues/9641
		Name: "bit union max1err dolt#9641",
		SetUpScript: []string{
			"CREATE TABLE report_card (id INT PRIMARY KEY, archived BIT(1))",
			"INSERT INTO report_card VALUES (1, 0)",
		},
		Assertions: []ScriptTestAssertion{
			{
				// max1err
				Query: `SELECT archived FROM report_card WHERE id = 1
					UNION ALL  
					SELECT 48 FROM report_card WHERE id = 1`,
				Expected: []sql.Row{{int64(0)}, {int64(48)}},
			},
		},
	},
	{
		// Regression test for https://github.com/dolthub/dolt/issues/9641
		Name:    "bit union comprehensive regression test dolt#9641",
		Dialect: "mysql",
		SetUpScript: []string{
			`CREATE TABLE t1 (
				id int PRIMARY KEY,
				name varchar(254),
				archived bit(1) NOT NULL DEFAULT b'0',
				archived_directly bit(1) NOT NULL DEFAULT b'0'
			)`,
			`CREATE TABLE t2 (
				id int PRIMARY KEY,
				name varchar(254),
				archived bit(1) NOT NULL DEFAULT b'0'
			)`,
			"INSERT INTO t1 VALUES (1, 'Card1', b'0', b'0')",
			"INSERT INTO t2 VALUES (2, 'Collection1', b'0')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `SELECT *,
					COUNT(*) OVER () AS total_count
				FROM (
					SELECT
						5 AS model_ranking,
						id,
						name,
						archived,
						archived_directly
					FROM t1
					WHERE archived_directly = FALSE
					
					UNION ALL
					
					SELECT
						7 AS model_ranking,
						id,
						name,
						archived,
						NULL AS archived_directly
					FROM t2
					WHERE archived = FALSE AND id <> 1
				) AS dummy_alias`,
				Expected: []sql.Row{
					{int64(5), int64(1), "Card1", uint64(0), int64(0), int64(2)},
					{int64(7), int64(2), "Collection1", uint64(0), nil, int64(2)},
				},
			},
		},
	},
	{
		Name:    "bits don't work on server",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (b bit(1));",
			"insert into t values (1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from t;",
				Expected: []sql.Row{{uint64(1)}},
			},
		},
	},
	{
		Name: "outer join finish unmatched right side",
		SetUpScript: []string{
			`
CREATE TABLE teams (
  team VARCHAR(100),
  namespace VARCHAR(100)
);`,
			"INSERT INTO teams(team, namespace) VALUES ('sam', 'sam1');",
			"INSERT INTO teams(team, namespace) VALUES ('sam', 'sam2');",
			"INSERT INTO teams(team, namespace) VALUES ('janos', 'janos1');",
			`CREATE TABLE traces (
  namespace VARCHAR(100),
  value INT
);`,
			"INSERT INTO traces(namespace, value) VALUES ('janos1', '400');",
			"INSERT INTO traces(namespace, value) VALUES ('0', '500');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT  team,  sum(value) FROM traces FULL OUTER JOIN teams ON teams.namespace = traces.namespace GROUP BY team;",
				Expected: []sql.Row{{"sam", nil}, {"janos", float64(400)}, {nil, float64(500)}},
			},
			{
				Query:    "SELECT  team,  sum(value) FROM teams FULL OUTER JOIN traces ON teams.namespace = traces.namespace GROUP BY team;",
				Expected: []sql.Row{{"sam", nil}, {"janos", float64(400)}, {nil, float64(500)}},
			},
		},
	},
	{
		Name: "keyless reverse index",
		SetUpScript: []string{
			"create table x (x int, key(x));",
			"insert into x values (0),(1)",
		},
		Query: "select * from x order by x desc limit 1",
		Expected: []sql.Row{
			{1},
		},
	},
	{
		Name: "filter pushdown through join uppercase name",
		SetUpScript: []string{
			"create table A (A int primary key);",
			"insert into A values (0),(1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:           "select /*+ JOIN_ORDER(A, b) */ * from A join A b where a.A = 1 and b.A = 1",
				ExpectedIndexes: []string{"primary", "primary"},
			},
		},
	},
	{
		Name: "issue 7958, update join uppercase table name validation",
		SetUpScript: []string{
			`
CREATE TABLE targetTable_test (
    source_id int PRIMARY KEY,
    value int
);`,
			`
CREATE TABLE sourceTable_test (
    id int PRIMARY KEY,
    value int
);`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `UPDATE targetTable_test
    JOIN sourceTable_test
    SET
        targetTable_test.value = sourceTable_test.value
    WHERE sourceTable_test.id = targetTable_test.source_id;
`,
				Expected: []sql.Row{{types.OkResult{
					RowsAffected: 0,
					InsertID:     0,
					Info: plan.UpdateInfo{
						Matched:  0,
						Updated:  0,
						Warnings: 0,
					},
				}}},
			},
			{
				Query: `UPDATE targetTable_test
    JOIN sourceTable_test
    ON sourceTAble_test.id = TARGETTABLE_test.source_id
    SET
        TARGETTABLE_test.value = SourceTable_test.value;
`,
				Expected: []sql.Row{{types.OkResult{
					RowsAffected: 0,
					InsertID:     0,
					Info: plan.UpdateInfo{
						Matched:  0,
						Updated:  0,
						Warnings: 0,
					},
				}}},
			},
		},
	},
	{
		Name: "histogram bucket merging error for implementor buckets",
		SetUpScript: []string{
			"CREATE TABLE xy (x int primary key, y varchar(10), key(y));",
			"insert into xy select x, 'x' from (with recursive inputs(x) as (select 1 union select x+1 from inputs where x < 5000) select * from inputs) dt",
			"analyze table xy",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select (select count(*) from information_schema.statistics) > 0",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select a.y from xy a join xy b on a.y = b.y limit 1",
				Expected: []sql.Row{{"x"}},
			},
			{
				Query:    "select y from xy where y = 'x' limit 1",
				Expected: []sql.Row{{"x"}},
			},
		},
	},
	{
		// https://github.com/dolthub/go-mysql-server/issues/2369
		Name: "auto_increment with self-referencing foreign key",
		SetUpScript: []string{
			`CREATE TABLE table1 (
	id int NOT NULL AUTO_INCREMENT,
	name text,
	parentId int DEFAULT NULL,
	PRIMARY KEY (id),
	CONSTRAINT myConstraint FOREIGN KEY (parentId) REFERENCES table1 (id) ON DELETE CASCADE
)`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO table1 (name, parentId) VALUES ('tbl1 row 1', NULL);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 1}}},
			},
			{
				Query:    "INSERT INTO table1 (name, parentId) VALUES ('tbl1 row 2', 1);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 2}}},
			},
			{
				Query:    "INSERT INTO table1 (name, parentId) VALUES ('tbl1 row 3', NULL);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 3}}},
			},
			{
				Query: "select * from table1",
				Expected: []sql.Row{
					{1, "tbl1 row 1", nil},
					{2, "tbl1 row 2", 1},
					{3, "tbl1 row 3", nil},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/go-mysql-server/issues/2349
		Name: "auto_increment with foreign key",
		SetUpScript: []string{
			"CREATE TABLE table1 (id int NOT NULL AUTO_INCREMENT primary key, name text)",
			`
CREATE TABLE table2 (
	id int NOT NULL AUTO_INCREMENT,
	name text,
	fk int,
	PRIMARY KEY (id),
	CONSTRAINT myConstraint FOREIGN KEY (fk) REFERENCES table1 (id)
)`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO table1 (name) VALUES ('tbl1 row 1');",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 1}}},
			},
			{
				Query:    "INSERT INTO table1 (name) VALUES ('tbl1 row 2');",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 2}}},
			},
		},
	},
	{
		Name: "index match only exact string, no prefix",
		SetUpScript: []string{
			"CREATE TABLE pk (x varchar(10) primary key)",
			"INSERT INTO pk values ('3'), ('30'), ('3#')",
			"CREATE TABLE uniq (y int primary key, x varchar(10), unique key (x))",
			"INSERT INTO uniq values (1,'3'), (2,'30'), (3,'3#')",
			"CREATE TABLE noncov (y int primary key, x varchar(10), z int, key (x))",
			"INSERT INTO noncov values (1,'3',1), (2,'30',2), (3,'3#',3)",
			"CREATE TABLE keyless (y int, x varchar(10),key (x))",
			"INSERT INTO keyless values (1,'3'), (2,'30'), (3,'3#')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from pk where x = '3'",
				Expected: []sql.Row{{"3"}},
			},
			{
				Query:    "delete from pk where x = '3' ",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select * from pk",
				Expected: []sql.Row{{"30"}, {"3#"}},
			},
			{
				Query:    "select * from uniq where x = '3'",
				Expected: []sql.Row{{1, "3"}},
			},
			{
				Query:    "delete from uniq where x = '3' ",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select * from uniq",
				Expected: []sql.Row{{2, "30"}, {3, "3#"}},
			},
			{
				Query:    "select * from noncov where x = '3'",
				Expected: []sql.Row{{1, "3", 1}},
			},
			{
				Query:    "delete from noncov where x = '3' ",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select * from noncov",
				Expected: []sql.Row{{2, "30", 2}, {3, "3#", 3}},
			},
			{
				Query:    "select * from keyless where x = '3'",
				Expected: []sql.Row{{1, "3"}},
			},
			{
				Query:    "delete from keyless where x = '3' ",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select * from keyless",
				Expected: []sql.Row{{2, "30"}, {3, "3#"}},
			},
		},
	},
	{
		Name: "keyless unique index bug",
		SetUpScript: []string{
			"CREATE TABLE mytable (pk int UNIQUE)",
			"INSERT INTO mytable values (1),(2),(3),(4)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT * FROM mytable order by pk",
				Expected: []sql.Row{{1}, {2}, {3}, {4}},
			},
			{
				Query:       "INSERT INTO mytable VALUES (1)",
				ExpectedErr: sql.ErrUniqueKeyViolation,
			},
			{
				Query: "INSERT INTO mytable VALUES (500000), (5000001)",
			},
			{
				Query: "SELECT count(*) FROM mytable where pk in (500000,5000001)",
			},
		},
	},
	{
		Name: "Dolt issue 7957, update join matched rows",
		SetUpScript: []string{
			`CREATE TABLE entity_test(
    id INT PRIMARY KEY,
    value INT
);`,
			"INSERT INTO entity_test (id, value) values (1,10), (2,20), (3,30);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `UPDATE entity_test
    JOIN (VALUES ROW(1, 10), ROW(2,20)) joined (id, value)
    ON joined.id = entity_test.id
SET entity_test.value = joined.value;`,
				Expected: []sql.Row{{types.OkResult{Info: plan.UpdateInfo{Matched: 2}}}},
			},
		},
	},
	{
		Name: "update join with update trigger different value",
		SetUpScript: []string{
			"create table t (i int primary key, j int, k int);",
			"insert into t values (1, 2, 3);",
			"create trigger trig before update on t for each row begin set new.j = 999; end;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "update t join (select 1 from t) as tt set t.k = 30;",
				Expected: []sql.Row{{NewUpdateResult(1, 1)}},
			},
			{
				Query:    "select * from t;",
				Expected: []sql.Row{{1, 999, 30}},
			},
			{
				Query:    "update t join (select 1, 2, 3, 4, 5 from t) as tt set t.k = 30;",
				Expected: []sql.Row{{NewUpdateResult(1, 0)}},
			},
			{
				Query:    "select * from t;",
				Expected: []sql.Row{{1, 999, 30}},
			},
		},
	},
	{
		Name: "update join with update trigger same value",
		SetUpScript: []string{
			"create table t (i int primary key, j int, k int);",
			"insert into t values (1, 2, 3);",
			"create trigger trig before update on t for each row begin set new.k = 999; end;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "update t join (select 1 from t) as tt set t.k = 30;",
				Expected: []sql.Row{{NewUpdateResult(1, 1)}},
			},
			{
				Query:    "select * from t;",
				Expected: []sql.Row{{1, 2, 999}},
			},
			{
				Query:    "update t join (select 1, 2, 3, 4, 5 from t) as tt set t.k = 30;",
				Expected: []sql.Row{{NewUpdateResult(1, 0)}},
			},
			{
				Query:    "select * from t;",
				Expected: []sql.Row{{1, 2, 999}},
			},
		},
	},
	{
		Name: "update join with update trigger",
		SetUpScript: []string{
			"create table t (i int primary key, j int, k int);",
			"insert into t values (1, 2, 3);",
			"create trigger trig before update on t for each row begin set new.k = 999; end;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "update t join (select 1 from t) as tt set t.k = 30 where t.i = 1;",
				Expected: []sql.Row{{NewUpdateResult(1, 1)}},
			},
			{
				Query:    "select * from t;",
				Expected: []sql.Row{{1, 2, 999}},
			},
			{
				// TODO: should throw can't update error
				Skip:     true,
				Query:    "update t join (select i, j, k from t) as tt set t.k = 30 where t.i = 1;",
				Expected: []sql.Row{{NewUpdateResult(1, 0)}},
			},
			{
				Query:    "update t join (select 1 from t) as tt set t.k = 30 limit 1;",
				Expected: []sql.Row{{NewUpdateResult(1, 0)}},
			},
			{
				Query:    "update t join (select 1 from t) as tt set t.k = 30 limit 1 offset 1;",
				Expected: []sql.Row{{NewUpdateResult(0, 0)}},
			},
		},
	},
	{
		Name: "update join with update trigger if condition",
		SetUpScript: []string{
			"CREATE TABLE test_users (\n" +
				"    `id` int NOT NULL AUTO_INCREMENT,\n" +
				"    `username` varchar(255) NOT NULL,\n" +
				"    `password` varchar(60) NOT NULL,\n" +
				"    `deleted` tinyint(1) DEFAULT '0',\n" +
				"    `favorite_number` INT,\n" +
				"    PRIMARY KEY (`id`)\n" +
				");",

			"CREATE TRIGGER test_on_delete_users\n" +
				"    BEFORE UPDATE ON test_users\n" +
				"    FOR EACH ROW\n" +
				"BEGIN\n" +
				"    IF NEW.`deleted` THEN\n" +
				"        SET NEW.`password` = '';\n" +
				"    END IF;\n" +
				"END ",

			"INSERT INTO test_users (username, password, deleted, favorite_number) VALUES ('john', 'doe', 0, 0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "UPDATE test_users JOIN (SELECT 1 FROM test_users) AS tu SET test_users.favorite_number = 42;",
				Expected: []sql.Row{{NewUpdateResult(1, 1)}},
			},
			{
				Query:    "select * from test_users;",
				Expected: []sql.Row{{1, "john", "doe", 0, 42}},
			},
			{
				Query:    "UPDATE test_users JOIN (SELECT 1 FROM test_users) AS tu SET test_users.favorite_number = 420;",
				Expected: []sql.Row{{NewUpdateResult(1, 1)}},
			},
			{
				Query:    "select * from test_users;",
				Expected: []sql.Row{{1, "john", "doe", 0, 420}},
			},
			{
				Query:    "UPDATE test_users JOIN (SELECT 1 FROM test_users) AS tu SET test_users.deleted = 1;",
				Expected: []sql.Row{{NewUpdateResult(1, 1)}},
			},
			{
				Query:    "select * from test_users;",
				Expected: []sql.Row{{1, "john", "", 1, 420}},
			},
		},
	},
	{
		Name: "missing indexes",
		SetUpScript: []string{
			`
create table t (
  id varchar(500),
  from_ varchar(500),
  to_ varchar(500),
  key (to_, from_),
  Primary key (id, from_, to_)
);`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:           "select * from t where to_ = 'L1' and from_ = 'L2'",
				Expected:        []sql.Row{},
				ExpectedIndexes: []string{"to_"},
			},
			{
				Query:           "select * from t where BIN_TO_UUID(id) = '0' and  to_ = 'L1' and from_ = 'L2'",
				Expected:        []sql.Row{},
				ExpectedIndexes: []string{"to_"},
			},
		},
	},
	{
		Name: "correctness test indexes",
		SetUpScript: []string{
			`
CREATE TABLE tab3 (
  pk int NOT NULL,
  col0 int,
  col1 float,
  col2 text,
  col3 int,
  col4 float,
  col5 text,
  PRIMARY KEY (pk),
  KEY idx_tab3_0 (col1),
  UNIQUE KEY idx_tab3_1 (col0),
  UNIQUE KEY idx_tab3_4 (col3,col4)
)`,
			"insert into tab3 values (1 , 101 , 83.86, 'pgprm', 50  , 58.56, 'nugdy')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select count(*) from tab3 WHERE (80 < col0 AND (((col0 BETWEEN 87 AND 9 OR (((col0 IS NULL)))))) AND (71.70 <= col1 OR 94 <= col0 AND ((66 > col0) OR (85 = col0 AND ((42.15 >= col1))) OR 30 = col0)));",
				Expected: []sql.Row{{0}},
			},
		},
	},
	{
		Name: "update exponential parsing",
		SetUpScript: []string{
			"create table a (a int primary key, b double);",
			"insert into a values (0, 0.0),(1, 1.0)",
			"update a set b = 5.0E-5 where a = 0",
			"update a set b = 5.0e-5 where a = 1",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from a",
				Expected: []sql.Row{{0, .00005}, {1, .00005}},
			},
		},
	},
	{
		Name: "set op schema merge",
		SetUpScript: []string{
			"create table `left` (i int primary key, j mediumint, k varchar(20));",
			"create table `right` (i int primary key, j bigint, k text);",
			"insert into `left` values (1,2, 'a')",
			"insert into `right` values (3,4, 'b')",

			"create table t1 (i int);",
			"insert into t1 values (1), (2), (3);",
			"create table t2 (i int);",
			"insert into t2 values (1), (3);",
			"create table t3 (j int);",
			"insert into t3 values (1), (3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select i, j from `left` union select i, j from `right`",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
					{
						Name: "j",
						Type: types.Int64,
					},
				},
				Expected: []sql.Row{{1, 2}, {3, 4}},
			},
			{
				Query: "select i, k from `left` union select i, k from `right`",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
					{
						Name: "k",
						Type: types.LongText,
					},
				},
				Expected: []sql.Row{{1, "a"}, {3, "b"}},
			},
			{
				Query: "select i, j, k from `left` union select i, j, k from `right`",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
					{
						Name: "j",
						Type: types.Int64,
					},
					{
						Name: "k",
						Type: types.LongText,
					},
				},
				Expected: []sql.Row{{1, 2, "a"}, {3, 4, "b"}},
			},
			{
				Query:       "select i, k from `left` union select i, j, k from `right`",
				ExpectedErr: planbuilder.ErrUnionSchemasDifferentLength,
			},
			{
				Query: "table t1 union table t2 order by i;",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "table t1 union table t2 order by i;",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "table t3 union table t1 order by j;",
				ExpectedColumns: sql.Schema{
					{
						Name: "j",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "select j as i from t3 union table t1 order by i;",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "table t1 union table t2 order by 1;",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "table t1 union table t3 order by 1;",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				// This looks wrong, but it actually matches MySQL
				Query: "table t1 union select i as j from t2 order by i;",
				ExpectedColumns: sql.Schema{
					{
						Name: "i",
						Type: types.Int32,
					},
				},
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query:       "table t1 union table t3 order by j;",
				ExpectedErr: sql.ErrColumnNotFound,
			},
			{
				Query:       "table t1 union table t2 order by t1.i;",
				ExpectedErr: planbuilder.ErrQualifiedOrderBy,
			},
			{
				Query:       "table t1 union table t2 order by t2.i;",
				ExpectedErr: planbuilder.ErrQualifiedOrderBy,
			},
			{
				Query:       "table t1 union table t3 order by t3.i;",
				ExpectedErr: planbuilder.ErrQualifiedOrderBy,
			},
			{
				Query:       "table t1 union table t3 order by t3.j;",
				ExpectedErr: planbuilder.ErrQualifiedOrderBy,
			},
			{
				Query:       "table t1 union table t3 order by t1.j;",
				ExpectedErr: planbuilder.ErrQualifiedOrderBy,
			},
		},
	},
	{
		Name: "intersection and except tests",
		SetUpScript: []string{
			"create table a (m int, n int);",
			"insert into a values (1,2), (2,3), (3,4);",
			"create table b (m int, n int);",
			"insert into b values (1,2), (1,3), (3,4);",
			"create table c (m int, n int);",
			"insert into c values (1,3), (1,3), (3,4);",
			"create table t1 (i int);",
			"insert into t1 values (1), (2), (3);",
			"create table t2 (i float);",
			"insert into t2 values (1.0), (1.99), (3.0);",
			"create table l (i int);",
			"insert into l values (1), (1), (1);",
			"create table r (i int);",
			"insert into r values (1);",
			"create table x (i int);",
			"insert into x values (1), (2), (3);",
			"create table y (i bigint);",
			"insert into y values (1), (3);",
		},
		Assertions: []ScriptTestAssertion{
			// Intersect tests
			{
				Query: "table a intersect table b order by m, n;",
				Expected: []sql.Row{
					{1, 2},
					{3, 4},
				},
			},
			{
				Query: "table a intersect table c order by m, n;",
				Expected: []sql.Row{
					{3, 4},
				},
			},
			{
				Query: "table c intersect distinct table c order by m, n;",
				Expected: []sql.Row{
					{1, 3},
					{3, 4},
				},
			},
			{
				Query: "table c intersect all table c order by m, n;",
				Expected: []sql.Row{
					{1, 3},
					{1, 3},
					{3, 4},
				},
			},
			{
				Query: "table a intersect table b intersect table c;",
				Expected: []sql.Row{
					{3, 4},
				},
			},
			{
				Query: "(table b order by m limit 1 offset 1) intersect (table c order by m limit 1);",
				Expected: []sql.Row{
					{1, 3},
				},
			},
			{
				Query: "table x intersect table y order by i;",
				Expected: []sql.Row{
					{1},
					{3},
				},
			},
			{
				Query: "table x intersect table y order by 1;",
				Expected: []sql.Row{
					{1},
					{3},
				},
			},
			{
				// Resulting type is string for some reason
				Skip:  true,
				Query: "table t1 intersect table t2;",
				Expected: []sql.Row{
					{1},
					{3},
				},
			},

			// Except tests
			{
				Query: "table a except table b order by m, n;",
				Expected: []sql.Row{
					{2, 3},
				},
			},
			{
				Query: "table a except table c order by m, n;",
				Expected: []sql.Row{
					{1, 2},
					{2, 3},
				},
			},
			{
				Query: "table b except table c order by m, n;",
				Expected: []sql.Row{
					{1, 2},
				},
			},
			{
				Query: "table c except distinct table a order by m, n;",
				Expected: []sql.Row{
					{1, 3},
				},
			},
			{
				Query: "table c except all table a order by m, n;",
				Expected: []sql.Row{
					{1, 3},
					{1, 3},
				},
			},
			{
				Query: "(table a order by m limit 1 offset 1) except (table c order by m limit 1);",
				Expected: []sql.Row{
					{2, 3},
				},
			},
			{
				Query: "table a except table b except table c;",
				Expected: []sql.Row{
					{2, 3},
				},
			},
			{
				Query: "table x except table y order by i;",
				Expected: []sql.Row{
					{2},
				},
			},
			{
				Query:    "table l except table r;",
				Expected: []sql.Row{},
			},
			{
				Query:    "table l except distinct table r;",
				Expected: []sql.Row{},
			},
			{
				Query: "table l except all table r;",
				Expected: []sql.Row{
					{1},
					{1},
				},
			},

			// Multiple set operation tests
			{
				Query: "table a except table b intersect table c order by m;",
				Expected: []sql.Row{
					{1, 2},
					{2, 3},
				},
			},
			{
				Query: "table a intersect table b except table c order by m;",
				Expected: []sql.Row{
					{1, 2},
				},
			},
			{
				Query: "table a union table a intersect table b except table c order by m;",
				Expected: []sql.Row{
					{1, 2},
					{2, 3},
				},
			},

			// CTE tests
			{
				Query: "with cte as (table a union table a intersect table b except table c order by m) select * from cte",
				Expected: []sql.Row{
					{1, 2},
					{2, 3},
				},
			},
			{
				Query: "with recursive cte(x) as (select 1) select * from cte",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "with recursive cte (x,y) as (select 1, 1 intersect select 1, 1 union select x + 1, y + 2 from cte where x < 5) select * from cte;",
				Expected: []sql.Row{
					{1, 1},
					{2, 3},
					{3, 5},
					{4, 7},
					{5, 9},
				},
			},
			{
				Query: "WITH RECURSIVE\n" +
					"    rt (foo) AS (\n" +
					"        SELECT 1 as foo\n" +
					"        UNION ALL\n" +
					"        SELECT foo + 1 as foo FROM rt WHERE foo < 5\n" +
					"    ),\n" +
					"        ladder (depth, foo) AS (\n" +
					"        SELECT 1 as depth, NULL as foo from rt\n" +
					"        UNION ALL\n" +
					"        SELECT ladder.depth + 1 as depth, rt.foo\n" +
					"        FROM ladder JOIN rt WHERE ladder.foo = rt.foo\n" +
					"    )\n" +
					"SELECT * FROM ladder;",
				Expected: []sql.Row{
					{1, nil},
					{1, nil},
					{1, nil},
					{1, nil},
					{1, nil},
				},
			},
			{
				Query:       "with recursive cte (x,y) as (select 1, 1 intersect select 1, 1 intersect select x + 1, y + 2 from cte where x < 5) select * from cte;",
				ExpectedErr: sql.ErrRecursiveCTEMissingUnion,
			},
			{
				Query:       "with recursive cte (x,y) as (select 1, 1 union select 1, 1 intersect select x + 1, y + 2 from cte where x < 5) select * from cte;",
				ExpectedErr: sql.ErrRecursiveCTENotUnion,
			},
		},
	},
	{
		Name: "create table casing",
		SetUpScript: []string{
			"create table t (lower varchar(20) primary key, UPPER varchar(20), MiXeD varchar(20), un_der varchar(20), `da-sh` varchar(20));",
			"insert into t values ('a','b','c','d','e')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `select * from t`,
				ExpectedColumns: sql.Schema{
					{
						Name: "lower",
						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
					},
					{
						Name: "UPPER",
						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
					},
					{
						Name: "MiXeD",
						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
					},
					{
						Name: "un_der",
						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
					},
					{
						Name: "da-sh",
						Type: types.MustCreateStringWithDefaults(sqltypes.VarChar, 20),
					},
				},
				Expected: []sql.Row{{"a", "b", "c", "d", "e"}},
			},
		},
	},
	{
		Name:    "alter table out of range value error of column type change",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, i2 int, key(i2));",
			"insert into t values (0,-1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       `alter table t modify column i2 int unsigned`,
				ExpectedErr: sql.ErrValueOutOfRange,
			},
		},
	},
	{
		Name: "topN stable output",
		SetUpScript: []string{
			"create table xy (x int primary key, y int)",
			"insert into xy values (1,0),(2,0),(3,0),(4,0)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from xy order by y asc limit 1",
				Expected: []sql.Row{{1, 0}},
			},
			{
				Query:    "select * from xy order by y asc limit 1 offset 1",
				Expected: []sql.Row{{2, 0}},
			},
			{
				Query:    "select * from xy order by y asc limit 1 offset 2",
				Expected: []sql.Row{{3, 0}},
			},
			{
				Query:    "select * from xy order by y asc limit 1 offset 3",
				Expected: []sql.Row{{4, 0}},
			},
			{
				Query:    "(select * from xy order by y asc limit 1 offset 1) union (select * from xy order by y asc limit 1 offset 2)",
				Expected: []sql.Row{{2, 0}, {3, 0}},
			},
			{
				Query:    "with recursive cte as ((select * from xy order by y asc limit 1 offset 1) union (select * from xy order by y asc limit 1 offset 2)) select * from cte",
				Expected: []sql.Row{{2, 0}, {3, 0}},
			},
		},
	},
	{
		Name: "failed statements data validation for INSERT, UPDATE",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT, INDEX (v1));",
			"INSERT INTO test VALUES (1,1), (4,4), (5,5);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "INSERT INTO test VALUES (2,2), (3,3), (1,1);",
				ExpectedErrStr: "duplicate primary key given: [1]",
			},
			{
				Query:    "SELECT * FROM test;",
				Expected: []sql.Row{{1, 1}, {4, 4}, {5, 5}},
			},
			{
				Query:          "UPDATE test SET pk = pk + 1 ORDER BY pk;",
				ExpectedErrStr: "duplicate primary key given: [5]",
			},
			{
				Query:    "SELECT * FROM test;",
				Expected: []sql.Row{{1, 1}, {4, 4}, {5, 5}},
			},
		},
	},
	{
		Name: "failed statements data validation for DELETE, REPLACE",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT, INDEX (v1));",
			"INSERT INTO test VALUES (1,1), (4,4), (5,5);",
			"CREATE TABLE test2 (pk BIGINT PRIMARY KEY, CONSTRAINT fk_test FOREIGN KEY (pk) REFERENCES test (v1));",
			"INSERT INTO test2 VALUES (4);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "DELETE FROM test WHERE pk > 0;",
				ExpectedErr: sql.ErrForeignKeyParentViolation,
			},
			{
				Query:    "SELECT * FROM test;",
				Expected: []sql.Row{{1, 1}, {4, 4}, {5, 5}},
			},
			{
				Query:    "SELECT * FROM test2;",
				Expected: []sql.Row{{4}},
			},
			{
				Query:       "REPLACE INTO test VALUES (1,7), (4,8), (5,9);",
				Dialect:     "mysql",
				ExpectedErr: sql.ErrForeignKeyParentViolation,
			},
			{
				Query:    "SELECT * FROM test;",
				Dialect:  "mysql",
				Expected: []sql.Row{{1, 1}, {4, 4}, {5, 5}},
			},
			{
				Query:    "SELECT * FROM test2;",
				Expected: []sql.Row{{4}},
			},
		},
	},
	{
		Name: "delete with in clause",
		SetUpScript: []string{
			"create table a (x int primary key)",
			"insert into a values (1), (3), (5)",
			"delete from a where x in (1, 3)",
		},
		Query: "select x from a order by 1",
		Expected: []sql.Row{
			{5},
		},
	},
	{
		Name: "sqllogictest evidence/slt_lang_aggfunc.test",
		SetUpScript: []string{
			"CREATE TABLE t1( x INTEGER, y VARCHAR(8) )",
			"INSERT INTO t1 VALUES(1,'true')",
			"INSERT INTO t1 VALUES(0,'false')",
			"INSERT INTO t1 VALUES(NULL,'NULL')",
		},
		Query: "SELECT count(DISTINCT x) FROM t1",
		Expected: []sql.Row{
			{2},
		},
	},
	{
		Name: "sqllogictest index/commute/10/slt_good_1.test",
		SetUpScript: []string{
			"CREATE TABLE tab0(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)",
			"INSERT INTO tab0 VALUES(0,42,58.92,'fnbtk',54,68.41,'xmttf')",
			"INSERT INTO tab0 VALUES(1,31,46.55,'sksjf',46,53.20,'wiuva')",
			"INSERT INTO tab0 VALUES(2,30,31.11,'oldqn',41,5.26,'ulaay')",
			"INSERT INTO tab0 VALUES(3,77,44.90,'pmsir',70,84.14,'vcmyo')",
			"INSERT INTO tab0 VALUES(4,23,95.26,'qcwxh',32,48.53,'rvtbr')",
			"INSERT INTO tab0 VALUES(5,43,6.75,'snvwg',3,14.38,'gnfxz')",
			"INSERT INTO tab0 VALUES(6,47,98.26,'bzzva',60,15.2,'imzeq')",
			"INSERT INTO tab0 VALUES(7,98,40.9,'lsrpi',78,66.30,'ephwy')",
			"INSERT INTO tab0 VALUES(8,19,15.16,'ycvjz',55,38.70,'dnkkz')",
			"INSERT INTO tab0 VALUES(9,7,84.4,'ptovf',17,2.46,'hrxsf')",
			"CREATE TABLE tab1(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)",
			"CREATE INDEX idx_tab1_0 on tab1 (col0)",
			"CREATE INDEX idx_tab1_1 on tab1 (col1)",
			"CREATE INDEX idx_tab1_3 on tab1 (col3)",
			"CREATE INDEX idx_tab1_4 on tab1 (col4)",
			"INSERT INTO tab1 SELECT * FROM tab0",
			"CREATE TABLE tab2(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)",
			"CREATE UNIQUE INDEX idx_tab2_1 ON tab2 (col4 DESC,col3)",
			"CREATE UNIQUE INDEX idx_tab2_2 ON tab2 (col3 DESC,col0)",
			"CREATE UNIQUE INDEX idx_tab2_3 ON tab2 (col3 DESC,col1)",
			"INSERT INTO tab2 SELECT * FROM tab0",
			"CREATE TABLE tab3(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)",
			"CREATE INDEX idx_tab3_0 ON tab3 (col3 DESC)",
			"INSERT INTO tab3 SELECT * FROM tab0",
			"CREATE TABLE tab4(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT)",
			"CREATE INDEX idx_tab4_0 ON tab4 (col0 DESC)",
			"CREATE UNIQUE INDEX idx_tab4_2 ON tab4 (col4 DESC,col3)",
			"CREATE INDEX idx_tab4_3 ON tab4 (col3 DESC)",
			"INSERT INTO tab4 SELECT * FROM tab0",
		},
		Query: "SELECT pk FROM tab2 WHERE 78 < col0 AND 19 < col3",
		Expected: []sql.Row{
			{7},
		},
	},
	{
		Name: "3 tables, linear join",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select ya from a join b on ya - 1= xb join c on xc = zb - 2",
				Expected: []sql.Row{{2}},
			},
		},
	},
	{
		Name: "3 tables, v join",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select za from a join b on ya - 1 = xb join c on xa = xc",
				Expected: []sql.Row{{3}},
			},
		},
	},
	{
		Name: "3 tables, linear join, indexes on A,C",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from a join b on xa = yb - 1 join c on yb - 1 = xc",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name: "4 tables, linear join",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from a join b on ya - 1 = xb join c on xb = xc join d on xc = xd",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name: "4 tables, linear join, index on D",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from a join b on ya = yb join c on yb = yc join d on yc - 1 = xd",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name: "4 tables, left join, indexes on all tables",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from a left join b on ya = yb left join c on yb = yc left join d on yc - 1 = xd",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name: "4 tables, linear join, index on B, D",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from a join b on ya - 1 = xb join c on yc = za - 1 join d on yc - 1 = xd",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name: "4 tables, all joined to A",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from a join b on xa = xb join c on ya - 1 = xc join d on za - 2 = xd",
				Expected: []sql.Row{{1}},
			},
		},
	},
	// {
	// 	Name: "4 tables, all joined to D",
	// 	SetUpScript: []string{
	// 		"create table a (xa int primary key, ya int, za int)",
	// 		"create table b (xb int primary key, yb int, zb int)",
	// 		"create table c (xc int primary key, yc int, zc int)",
	// 		"create table d (xd int primary key, yd int, zd int)",
	// 		"insert into a values (1,2,3)",
	// 		"insert into b values (1,2,3)",
	// 		"insert into c values (1,2,3)",
	// 		"insert into d values (1,2,3)",
	// 	},
	// 	Assertions: []ScriptTestAssertion{
	// 		{
	// 			// gives an error in mysql, a needs an alias
	// 			Query: "select xa from d join a on yd = xa join c on yd = xc join a on xa = yd",
	// 			Expected: []sql.Row{{1}},
	// 		},
	// 	},
	// },
	{
		Name: "4 tables, all joined to D",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select xa from d join a on yd - 1 = xa join c on zd - 2 = xc join b on xb = zd - 2",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name: "5 tables, complex join conditions",
		SetUpScript: []string{
			"create table a (xa int primary key, ya int, za int)",
			"create table b (xb int primary key, yb int, zb int)",
			"create table c (xc int primary key, yc int, zc int)",
			"create table d (xd int primary key, yd int, zd int)",
			"create table e (xe int, ye int, ze int, primary key(xe, ye))",
			"insert into a values (1,2,3)",
			"insert into b values (1,2,3)",
			"insert into c values (1,2,3)",
			"insert into d values (1,2,3)",
			"insert into e values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `select xa from a
									join b on ya - 1 = xb
									join c on xc = za - 2
									join d on xd = yb - 1
									join e on xe = zb - 2 and ye = yc`,
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name:    "UUIDs used in the wild.",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET @uuid = '6ccd780c-baba-1026-9564-5b8c656024db'",
			"SET @binuuid = '0011223344556677'",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    `SELECT IS_UUID(UUID())`,
				Expected: []sql.Row{{true}},
			},
			{
				Query:    `SELECT IS_UUID(@uuid)`,
				Expected: []sql.Row{{true}},
			},
			{
				Query:    `SELECT BIN_TO_UUID(UUID_TO_BIN(@uuid))`,
				Expected: []sql.Row{{"6ccd780c-baba-1026-9564-5b8c656024db"}},
			},
			{
				Query:    `SELECT BIN_TO_UUID(UUID_TO_BIN(@uuid, 1), 1)`,
				Expected: []sql.Row{{"6ccd780c-baba-1026-9564-5b8c656024db"}},
			},
			{
				Query:    `SELECT UUID_TO_BIN(NULL)`,
				Expected: []sql.Row{{nil}},
			},
			{
				Query:    `SELECT HEX(UUID_TO_BIN(@uuid))`,
				Expected: []sql.Row{{"6CCD780CBABA102695645B8C656024DB"}},
			},
			{
				Query:       `SELECT UUID_TO_BIN(123)`,
				ExpectedErr: sql.ErrUuidUnableToParse,
			},
			{
				Query:       `SELECT BIN_TO_UUID(123)`,
				ExpectedErr: sql.ErrUuidUnableToParse,
			},
			{
				Query:    `SELECT BIN_TO_UUID(X'00112233445566778899aabbccddeeff')`,
				Expected: []sql.Row{{"00112233-4455-6677-8899-aabbccddeeff"}},
			},
			{
				Query:    `SELECT BIN_TO_UUID('0011223344556677')`,
				Expected: []sql.Row{{"30303131-3232-3333-3434-353536363737"}},
			},
			{
				Query:    `SELECT BIN_TO_UUID(@binuuid)`,
				Expected: []sql.Row{{"30303131-3232-3333-3434-353536363737"}},
			},
		},
	},
	{
		Name:    "Test cases on select into statement",
		Dialect: "mysql",
		SetUpScript: []string{
			"SELECT * FROM (VALUES ROW(22,44,88)) AS t INTO @x,@y,@z",
			"CREATE TABLE tab1 (id int primary key, v1 int)",
			"INSERT INTO tab1 VALUES (1, 1), (2, 3), (3, 6)",
			"SELECT id FROM tab1 ORDER BY id DESC LIMIT 1 INTO @myVar",
			"CREATE TABLE tab2 (i2 int primary key, s text)",
			"INSERT INTO tab2 VALUES (1, 'b'), (2, 'm'), (3, 'g')",
			"SELECT m.id, t.s FROM tab1 m JOIN tab2 t on m.id = t.i2 ORDER BY t.s DESC LIMIT 1 INTO @myId, @myText",
			// TODO: union statement does not handle order by and limit clauses
			// "SELECT id FROM tab1 UNION select s FROM tab2 LIMIT 1 INTO @myUnion",
			"SELECT id FROM tab1 WHERE id > 3 UNION select s FROM tab2 WHERE s < 'f' INTO @mustSingleVar",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:                         `SELECT 1 INTO @abc`,
				SkipResultCheckOnServerEngine: true,
				Expected:                      []sql.Row{{types.NewOkResult(1)}},
				ExpectedColumns:               nil,
			},
			{
				Query:    `SELECT @abc`,
				Expected: []sql.Row{{int8(1)}},
			},
			{
				Query:    `SELECT @z, @x, @y`,
				Expected: []sql.Row{{88, 22, 44}},
			},
			{
				Query:    `SELECT @myVar, @mustSingleVar`,
				Expected: []sql.Row{{3, "b"}},
			},
			{
				Query:    `SELECT @myId, @myText, @myUnion`,
				Expected: []sql.Row{{2, "m", nil}},
			},
			{
				Query:       `SELECT id FROM tab1 ORDER BY id DESC INTO @myvar`,
				ExpectedErr: sql.ErrMoreThanOneRow,
			},
			{
				Query:       `SELECT id INTO DUMPFILE 'baddump.out' FROM tab1 ORDER BY id DESC LIMIT 15`,
				ExpectedErr: sql.ErrMoreThanOneRow,
			},
			{
				Query:       `select 1, 2, 3 into @my1, @my2`,
				ExpectedErr: sql.ErrColumnNumberDoesNotMatch,
			},
			{
				Query:          `SELECT id, v1 INTO @myFirstVar FROM tab1 ORDER BY id DESC LIMIT 1 INTO @mySecondVar`,
				ExpectedErrStr: "Multiple INTO clauses in one query block at position 84 near '@mySecondVar'",
			},
			{
				Query:          `SELECT id FROM tab1 WHERE id > 3 UNION select s INTO @mustSingleVar FROM tab2 WHERE s < 'f' ORDER BY s DESC`,
				ExpectedErrStr: "INTO clause is not allowed at position 98 near 'ORDER'",
			},
		},
	},
	{
		Name: "CrossDB Queries",
		SetUpScript: []string{
			"CREATE DATABASE test",
			"CREATE TABLE test.x (pk int primary key)",
			"insert into test.x values (1),(2),(3)",
			"DELETE FROM test.x WHERE pk=2",
			"UPDATE test.x set pk=300 where pk=3",
			"create table a (xa int primary key, ya int, za int)",
			"insert into a values (1,2,3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT pk from test.x",
				Expected: []sql.Row{{1}, {300}},
			},
			{
				Query:    "SELECT * from a",
				Expected: []sql.Row{{1, 2, 3}},
			},
		},
	},
	{
		// All DECLARE statements are only allowed under BEGIN/END blocks
		Name: "Top-level DECLARE statements",
		Assertions: []ScriptTestAssertion{
			{
				Query:       "DECLARE no_such_table CONDITION FOR SQLSTATE '42S02'",
				ExpectedErr: sql.ErrSyntaxError,
			},
			{
				Query:       "DECLARE no_such_table CONDITION FOR 1051",
				ExpectedErr: sql.ErrSyntaxError,
			},
			{
				Query:       "DECLARE a CHAR(16)",
				ExpectedErr: sql.ErrSyntaxError,
			},
			{
				Query:       "DECLARE cur2 CURSOR FOR SELECT i FROM test.t2",
				ExpectedErr: sql.ErrSyntaxError,
			},
			{
				Query:       "DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE",
				ExpectedErr: sql.ErrSyntaxError,
			},
		},
	},
	{
		Name:    "last_insert_uuid() behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table varchar36 (pk varchar(36) primary key default (UUID()), i int);",
			"create table char36 (pk char(36) primary key default (UUID()), i int);",
			"create table varbinary16 (pk varbinary(16) primary key default (UUID_to_bin(UUID())), i int);",
			"create table binary16 (pk binary(16) primary key default (UUID_to_bin(UUID())), i int);",
			"create table binary16swap (pk binary(16) primary key default (UUID_to_bin(UUID(), true)), i int);",
			"create table invalid (pk int primary key, c1 varchar(36) default (UUID()));",
			"create table prepared (uuid char(36) default (UUID()), ai int auto_increment, c1 varchar(100), primary key (uuid, ai));",
		},
		Assertions: []ScriptTestAssertion{
			// The initial value of last_insert_uuid() is an empty string
			{
				Query:    "select last_insert_uuid()",
				Expected: []sql.Row{{""}},
			},

			// invalid table – UUID default is not a primary key, so last_insert_uuid() doesn't get udpated
			{
				Query:    "insert into invalid values (1, DEFAULT);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select last_insert_uuid()",
				Expected: []sql.Row{{""}},
			},
			{
				Query:    "insert into invalid values (2, UUID());",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select last_insert_uuid()",
				Expected: []sql.Row{{""}},
			},

			// varchar(36) test cases...
			{
				Query:    "insert into varchar36 values (DEFAULT, 1);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from varchar36 where i=1);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into varchar36 values (UUID(), 2), (UUID(), 3);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2}}},
			},
			{
				// last_insert_uuid() reports the first UUID() generated in the last insert statement
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from varchar36 where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into varchar36 values ('notta-uuid', 4);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				// The previous insert didn't generate a UUID, so last_insert_uuid() doesn't get updated
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from varchar36 where i=2);",
				Expected: []sql.Row{{true, true}},
			},

			// char(36) test cases...
			{
				Query:    "insert into char36 values (DEFAULT, 1);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from char36 where i=1);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into char36 values (UUID(), 2), (UUID(), 3);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2}}},
			},
			{
				// last_insert_uuid() reports the first UUID() generated in the last insert statement
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from char36 where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into char36 values ('notta-uuid', 4);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				// The previous insert didn't generate a UUID, so last_insert_uuid() doesn't get updated
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from char36 where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into char36 (i) values (5);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from char36 where i=5);",
				Expected: []sql.Row{{true, true}},
			},

			// varbinary(16) test cases...
			{
				Query:    "insert into varbinary16 values (DEFAULT, 1);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from varbinary16 where i=1);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into varbinary16 values (UUID_to_bin(UUID()), 2), (UUID_to_bin(UUID()), 3);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2}}},
			},
			{
				// last_insert_uuid() reports the first UUID() generated in the last insert statement
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from varbinary16 where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into varbinary16 values ('notta-uuid', 4);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				// The previous insert didn't generate a UUID, so last_insert_uuid() doesn't get updated
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from varbinary16 where i=2);",
				Expected: []sql.Row{{true, true}},
			},

			// binary(16) test cases...
			{
				Query:    "insert into binary16 values (DEFAULT, 1);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from binary16 where i=1);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into binary16 values (UUID_to_bin(UUID()), 2), (UUID_to_bin(UUID()), 3);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2}}},
			},
			{
				// last_insert_uuid() reports the first UUID() generated in the last insert statement
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from binary16 where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into binary16 values ('notta-uuid', 4);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				// The previous insert didn't generate a UUID, so last_insert_uuid() doesn't get updated
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from binary16 where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into binary16 (i) values (5);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk) from binary16 where i=5);",
				Expected: []sql.Row{{true, true}},
			},

			// binary(16) with UUID_to_bin swap test cases...
			{
				Query:    "insert into binary16swap values (DEFAULT, 1);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk, true) from binary16swap where i=1);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into binary16swap values (UUID_to_bin(UUID(), true), 2), (UUID_to_bin(UUID(), true), 3);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2}}},
			},
			{
				// last_insert_uuid() reports the first UUID() generated in the last insert statement
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk, true) from binary16swap where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into binary16swap values ('notta-uuid', 4);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				// The previous insert didn't generate a UUID, so last_insert_uuid() doesn't get updated
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk, true) from binary16swap where i=2);",
				Expected: []sql.Row{{true, true}},
			},
			{
				Query:    "insert into binary16swap (i) values (5);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select bin_to_uuid(pk, true) from binary16swap where i=5);",
				Expected: []sql.Row{{true, true}},
			},

			// INSERT INTO ... SELECT ... Tests
			{
				// If we populate the UUID column (pk) with its implicit default, then it updates last_insert_uuid()
				Query:    "insert into varchar36 (i) select 42 from dual;",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from varchar36 where i=42);",
				Expected: []sql.Row{{true, true}},
			},
			{
				// If all values come from another table, the auto_uuid value shouldn't be generated, so last_insert_uuid() doesn't change
				Query:    "insert into varchar36 (pk, i) (select 'one', 101 from dual union all select 'two', 202);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2}}},
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select pk from varchar36 where i=42);",
				Expected: []sql.Row{{true, true}},
			},

			// Prepared statements
			{
				// Test with an insert statement that implicit uses the UUID column default
				Query:    `prepare stmt1 from "insert into prepared (c1) values ('odd'), ('even')";`,
				Expected: []sql.Row{{types.OkResult{Info: plan.PrepareInfo{}}}},
			},
			{
				Query:                         "execute stmt1;",
				Expected:                      []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 1}}},
				SkipResultCheckOnServerEngine: true, // Server engine returns []sql.Row{}
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select uuid from prepared where ai=1), last_insert_id();",
				Expected: []sql.Row{{true, true, uint64(1)}},
			},
			{
				// Executing the prepared statement a second time should refresh last_insert_uuid()
				Query:                         "execute stmt1;",
				Expected:                      []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 3}}},
				SkipResultCheckOnServerEngine: true, // Server engine returns []sql.Row{}
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select uuid from prepared where ai=3), last_insert_id();",
				Expected: []sql.Row{{true, true, uint64(3)}},
			},

			{
				// Test with an insert statement that explicitly uses the UUID column default
				Query:    `prepare stmt2 from "insert into prepared (uuid, c1) values (DEFAULT, 'more'), (DEFAULT, 'less')";`,
				Expected: []sql.Row{{types.OkResult{Info: plan.PrepareInfo{}}}},
			},
			{
				Query:                         "execute stmt2;",
				Expected:                      []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 5}}},
				SkipResultCheckOnServerEngine: true, // Server engine returns []sql.Row{}
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select uuid from prepared where ai=5), last_insert_id();",
				Expected: []sql.Row{{true, true, uint64(5)}},
			},
			{
				// Executing the prepared statement a second time should refresh last_insert_uuid()
				Query:                         "execute stmt2;",
				Expected:                      []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 7}}},
				SkipResultCheckOnServerEngine: true, // Server engine returns []sql.Row{}
			},
			{
				Query:    "select is_uuid(last_insert_uuid()), last_insert_uuid() = (select uuid from prepared where ai=7), last_insert_id();",
				Expected: []sql.Row{{true, true, uint64(7)}},
			},
		},
	},
	{
		Name:    "last_insert_id() behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table a (x int primary key auto_increment, y int)",
			"create table b (x int primary key)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(0)}},
			},
			{
				Query:    "insert into a (x,y) values (1,1)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 1}}},
			},
			{
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(0)}},
			},
			{
				Query:    "insert into a (y) values (1)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 2}}},
			},
			{
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(2)}},
			},
			{
				Query:    "insert into a (y) values (2), (3)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 3}}},
			},
			{
				// last_insert_id() should return the insert id of the *first* value inserted in the last statement
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(3)}},
			},
			{
				Query:    "insert into b (x) values (1), (2)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 0}}},
			},
			{
				// The above query doesn't have an auto increment column, so last_insert_id is unchanged
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(3)}},
			},
			{
				Query: "insert into a (x, y) values (-100, 10)",
				Expected: []sql.Row{{types.OkResult{
					RowsAffected: 1,
					InsertID:     math.MaxUint64 - 100 + 1,
				}}},
			},
			{
				// last_insert_id() should not update for manually inserted values
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(3)}},
			},
			{
				Query: "insert into a (x, y) values (100, 10)",
				Expected: []sql.Row{{types.OkResult{
					RowsAffected: 1,
					InsertID:     100,
				}}},
			},
			{
				// last_insert_id() should not update for manually inserted values
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(3)}},
			},
		},
	},
	{
		Name:    "last_insert_id(expr) behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table a (x int primary key auto_increment, y int)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "insert into a (y) values (1)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 1}}},
			},
			{
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(1)}},
			},
			{
				Query:    "insert into a (x, y) values (1, 1) on duplicate key update y = 2, x=last_insert_id(x)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 1}}},
			},
			{
				Query:    "select * from a order by x",
				Expected: []sql.Row{{1, 2}},
			},
			{
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(1)}},
			},
			{
				Query:    "insert into a (y) values (100)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 2}}},
			},
			{
				Query:    "select last_insert_id()",
				Expected: []sql.Row{{uint64(2)}},
			},
		},
	},
	{
		Name:    "last_insert_id(default) behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (pk int primary key auto_increment, i int default 0)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "insert into t(pk) values (default);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 1}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(1)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
				},
			},

			{
				Query:    "insert into t(pk) values (default), (default), (default), (default), (default);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 5, InsertID: 2}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(2)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
				},
			},

			{
				Query:    "insert into t(pk) values (10), (default);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 2, InsertID: 10}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(11)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
					{10, 0},
					{11, 0},
				},
			},

			{
				Query:    "insert into t(pk) values (20), (default), (default);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 3, InsertID: 20}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(21)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
					{10, 0},
					{11, 0},
					{20, 0},
					{21, 0},
					{22, 0},
				},
			},

			{
				Query:    "insert into t(i) values (100);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 23}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(23)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
					{10, 0},
					{11, 0},
					{20, 0},
					{21, 0},
					{22, 0},
					{23, 100},
				},
			},

			{
				Query:    "insert into t(i, pk) values (200, default);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 24}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(24)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
					{10, 0},
					{11, 0},
					{20, 0},
					{21, 0},
					{22, 0},
					{23, 100},
					{24, 200},
				},
			},

			{
				Query:    "insert into t(pk) values (null);",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 25}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(25)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
					{10, 0},
					{11, 0},
					{20, 0},
					{21, 0},
					{22, 0},
					{23, 100},
					{24, 200},
					{25, 0},
				},
			},

			{
				Query:    "insert into t values ();",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 26}}},
			},
			{
				Query: "select last_insert_id()",
				Expected: []sql.Row{
					{uint64(26)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, 0},
					{2, 0},
					{3, 0},
					{4, 0},
					{5, 0},
					{6, 0},
					{10, 0},
					{11, 0},
					{20, 0},
					{21, 0},
					{22, 0},
					{23, 100},
					{24, 200},
					{25, 0},
					{26, 0},
				},
			},
		},
	},
	{
		Name:    "row_count() behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table b (x int primary key)",
			"insert into b values (1), (2), (3), (4)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{4}},
			},
			{
				Query:    "replace into b values (1)",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{2}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{-1}},
			},
			{
				Query:    "select count(*) from b",
				Expected: []sql.Row{{4}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{-1}},
			},
			{
				Query: "update b set x = x + 10 where x <> 2",
				Expected: []sql.Row{{types.OkResult{
					RowsAffected: 3,
					Info: plan.UpdateInfo{
						Matched: 3,
						Updated: 3,
					},
				}}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{3}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{-1}},
			},
			{
				Query:    "delete from b where x <> 2",
				Expected: []sql.Row{{types.NewOkResult(3)}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{3}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{-1}},
			},
			{
				Query:    "alter table b add column y int null",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{0}},
			},
			{
				Query:    "select row_count()",
				Expected: []sql.Row{{-1}},
			},
		},
	},
	{
		Name: "same alias names for result column name and alias table column name",
		SetUpScript: []string{
			"CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)",
			"INSERT INTO tab0 VALUES(83,0,38)",
			"INSERT INTO tab0 VALUES(26,0,79)",
			"INSERT INTO tab0 VALUES(43,81,24)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT + 13 AS col0 FROM tab0 GROUP BY tab0.col0",
				Expected: []sql.Row{{13}, {13}, {13}},
			},
			{
				Query:    "SELECT 82 col1 FROM tab0 AS cor0 GROUP BY cor0.col1",
				Expected: []sql.Row{{82}, {82}},
			},
			{
				Query:    "SELECT - cor0.col2 * - col2 AS col1 FROM tab0 AS cor0 GROUP BY col2, cor0.col1",
				Expected: []sql.Row{{1444}, {6241}, {576}},
			},
			{
				Query:    "SELECT ALL + 40 col1 FROM tab0 AS cor0 GROUP BY cor0.col1",
				Expected: []sql.Row{{40}, {40}},
			},
			{
				Query:    "SELECT DISTINCT - cor0.col1 col1 FROM tab0 AS cor0 GROUP BY cor0.col1, cor0.col2",
				Expected: []sql.Row{{-81}, {0}},
			},
			{
				Query:    "SELECT DISTINCT ( cor0.col0 ) - col0 AS col2 FROM tab0 AS cor0 GROUP BY cor0.col2, cor0.col0, cor0.col0",
				Expected: []sql.Row{{0}},
			},
		},
	},
	{
		Name:    "found_rows() behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table b (x int primary key)",
			"insert into b values (1), (2), (3), (4)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from b where x < 2",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select * from b",
				Expected: []sql.Row{{1}, {2}, {3}, {4}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{4}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select * from b order by x  limit 3",
				Expected: []sql.Row{{1}, {2}, {3}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{3}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select * from b order by x limit 100",
				Expected: []sql.Row{{1}, {2}, {3}, {4}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{4}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select sql_calc_found_rows * from b order by x limit 3",
				Expected: []sql.Row{{1}, {2}, {3}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{4}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select sql_calc_found_rows * from b where x <= 2 order by x limit 1",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{2}},
			},
			{
				Query:    "select sql_calc_found_rows * from b where x <= 2 order by x limit 1",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "insert into b values (10), (11), (12), (13)",
				Expected: []sql.Row{{types.NewOkResult(4)}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{2}},
			},
			{
				Query:    "update b set x = x where x < 40",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0, InsertID: 0, Info: plan.UpdateInfo{Matched: 8}}}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{8}},
			},
			{
				Query:    "update b set x = x where x > 10",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0, InsertID: 0, Info: plan.UpdateInfo{Matched: 3}}}},
			},
			{
				Query:    "select found_rows()",
				Expected: []sql.Row{{3}},
			},
		},
	},
	{
		Name: "INSERT INTO ... SELECT with AUTO_INCREMENT",
		SetUpScript: []string{
			"create table ai (pk int primary key auto_increment, c0 int);",
			"create table other (pk int primary key);",
			"insert into other values (1), (2), (3)",
			"insert into ai (c0) select * from other order by other.pk;",
		},
		Query: "select * from ai;",
		Expected: []sql.Row{
			{1, 1},
			{2, 2},
			{3, 3},
		},
	},
	{
		Name: "Indexed Join On Keyless Table",
		SetUpScript: []string{
			"create table l (pk int primary key, c0 int, c1 int);",
			"create table r (c0 int, c1 int, third int);",
			"create index r_c0 on r (c0);",
			"create index r_c1 on r (c1);",
			"create index r_third on r (third);",
			"insert into l values (0, 0, 0), (1, 0, 1), (2, 1, 0), (3, 0, 2), (4, 2, 0), (5, 1, 2), (6, 2, 1), (7, 2, 2);",
			"insert into l values (256, 1024, 4096);",
			"insert into r values (1, 1, -1), (2, 2, -1), (2, 2, -1);",
			"insert into r values (-1, -1, 256);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select pk, l.c0, l.c1 from l join r on l.c0 = r.c0 or l.c1 = r.c1 order by 1, 2, 3;",
				Expected: []sql.Row{
					{1, 0, 1},
					{2, 1, 0},
					{3, 0, 2},
					{3, 0, 2},
					{4, 2, 0},
					{4, 2, 0},
					{5, 1, 2},
					{5, 1, 2},
					{5, 1, 2},
					{6, 2, 1},
					{6, 2, 1},
					{6, 2, 1},
					{7, 2, 2},
					{7, 2, 2},
				},
			},
			{
				Query: "select pk, l.c0, l.c1 from l join r on l.c0 = r.c0 or l.c1 = r.c1 or l.pk = r.third order by 1, 2, 3;",
				Expected: []sql.Row{
					{1, 0, 1},
					{2, 1, 0},
					{3, 0, 2},
					{3, 0, 2},
					{4, 2, 0},
					{4, 2, 0},
					{5, 1, 2},
					{5, 1, 2},
					{5, 1, 2},
					{6, 2, 1},
					{6, 2, 1},
					{6, 2, 1},
					{7, 2, 2},
					{7, 2, 2},
					{256, 1024, 4096},
				},
			},
			{
				Query: "select pk, l.c0, l.c1 from l join r on l.c0 = r.c0 or l.c1 < 4 and l.c1 = r.c1 or l.c1 >= 4 and l.c1 = r.c1 order by 1, 2, 3;",
				Expected: []sql.Row{
					{1, 0, 1},
					{2, 1, 0},
					{3, 0, 2},
					{3, 0, 2},
					{4, 2, 0},
					{4, 2, 0},
					{5, 1, 2},
					{5, 1, 2},
					{5, 1, 2},
					{6, 2, 1},
					{6, 2, 1},
					{6, 2, 1},
					{7, 2, 2},
					{7, 2, 2},
				},
			},
		},
	},
	{
		Name:    "Group Concat Queries",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE x (pk int)",
			"INSERT INTO x VALUES (1),(2),(3),(4),(NULL)",

			"create table t (o_id int, attribute longtext, value longtext)",
			"INSERT INTO t VALUES (2, 'color', 'red'), (2, 'fabric', 'silk')",
			"INSERT INTO t VALUES (3, 'color', 'green'), (3, 'shape', 'square')",

			"create table nulls(pk int)",
			"INSERT INTO nulls VALUES (NULL)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    `SELECT group_concat(pk ORDER BY pk) FROM x;`,
				Expected: []sql.Row{{"1,2,3,4"}},
			},
			{
				Query:    `SELECT group_concat(DISTINCT pk ORDER BY pk) FROM x;`,
				Expected: []sql.Row{{"1,2,3,4"}},
			},
			{
				Query:    `SELECT group_concat(DISTINCT pk ORDER BY pk SEPARATOR '-') FROM x;`,
				Expected: []sql.Row{{"1-2-3-4"}},
			},
			{
				Query:    "SELECT group_concat(`attribute` ORDER BY `attribute`) FROM t group by o_id order by o_id asc",
				Expected: []sql.Row{{"color,fabric"}, {"color,shape"}},
			},
			{
				Query:    "SELECT group_concat(DISTINCT `attribute` ORDER BY value DESC SEPARATOR ';') FROM t group by o_id order by o_id asc",
				Expected: []sql.Row{{"fabric;color"}, {"shape;color"}},
			},
			{
				Query:    "SELECT group_concat(DISTINCT `attribute` ORDER BY `attribute`) FROM t",
				Expected: []sql.Row{{"color,fabric,shape"}},
			},
			{
				Query:    "SELECT group_concat(`attribute` ORDER BY `attribute`) FROM t",
				Expected: []sql.Row{{"color,color,fabric,shape"}},
			},
			{
				Query:    `SELECT group_concat((SELECT 2)) FROM x;`,
				Expected: []sql.Row{{"2,2,2,2,2"}},
			},
			{
				Query:    `SELECT group_concat(DISTINCT (SELECT 2)) FROM x;`,
				Expected: []sql.Row{{"2"}},
			},
			{
				Query:    "SELECT group_concat(DISTINCT `attribute` ORDER BY `attribute` ASC) FROM t",
				Expected: []sql.Row{{"color,fabric,shape"}},
			},
			{
				Query:    "SELECT group_concat(DISTINCT `attribute` ORDER BY `attribute` DESC) FROM t",
				Expected: []sql.Row{{"shape,fabric,color"}},
			},
			{
				Query:    `SELECT group_concat(pk) FROM nulls`,
				Expected: []sql.Row{{nil}},
			},
			{
				Query:       `SELECT group_concat((SELECT * FROM t LIMIT 1)) from t`,
				ExpectedErr: sql.ErrInvalidOperandColumns,
			},
			{
				Query:       `SELECT group_concat((SELECT * FROM x)) from t`,
				ExpectedErr: sql.ErrExpectedSingleRow,
			},
			{
				Query:    "SELECT group_concat(`attribute`) FROM t where o_id=2 order by attribute",
				Expected: []sql.Row{{"color,fabric"}},
			},
			{
				Query:    "SELECT group_concat(DISTINCT `attribute` ORDER BY value DESC SEPARATOR ';') FROM t group by o_id order by o_id asc",
				Expected: []sql.Row{{"fabric;color"}, {"shape;color"}},
			},
			{
				Query:    "SELECT group_concat(o_id order by o_id) FROM t WHERE `attribute`='color' order by o_id",
				Expected: []sql.Row{{"2,3"}},
			},
			{
				Query:    "SELECT group_concat(attribute separator '') FROM t WHERE o_id=2 ORDER BY attribute",
				Expected: []sql.Row{{"colorfabric"}},
			},
		},
	},
	{
		Name:    "Group Concat with Subquery in ORDER BY",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test_data (id INT PRIMARY KEY, name VARCHAR(50), age INT, category VARCHAR(10))",
			`INSERT INTO test_data VALUES  
(1, 'Alice', 25, 'A'),
(2, 'Bob', 30, 'B'), 
(3, 'Charlie', 22, 'A'), 
(4, 'Diana', 28, 'C'), 
(5, 'Eve', 35, 'B'), 
(6, 'Frank', 26, 'A')`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT category, group_concat(name ORDER BY (SELECT COUNT(*) FROM test_data t2 WHERE t2.category = test_data.category AND t2.age < test_data.age)) FROM test_data GROUP BY category ORDER BY category",
				Expected: []sql.Row{{"A", "Charlie,Alice,Frank"}, {"B", "Bob,Eve"}, {"C", "Diana"}},
			},
			{
				Query:    "SELECT group_concat(name ORDER BY (SELECT AVG(age) FROM test_data t2 WHERE t2.category = test_data.category), id) FROM test_data;",
				Expected: []sql.Row{{"Alice,Charlie,Frank,Diana,Bob,Eve"}},
			},
			{
				Query:    "SELECT category, group_concat(name ORDER BY (SELECT MAX(age) FROM test_data t2 WHERE t2.id <= test_data.id)) FROM test_data GROUP BY category ORDER BY category",
				Expected: []sql.Row{{"A", "Alice,Charlie,Frank"}, {"B", "Bob,Eve"}, {"C", "Diana"}},
			},
		},
	},
	{
		Name:    "Group Concat with Subquery in ORDER BY - Additional Edge Cases",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE products (id INT PRIMARY KEY, name VARCHAR(50), price DECIMAL(10,2), category_id INT, supplier_id INT)",
			"CREATE TABLE categories (id INT PRIMARY KEY, name VARCHAR(50), priority INT)",
			"CREATE TABLE suppliers (id INT PRIMARY KEY, name VARCHAR(50), rating INT)",
			"INSERT INTO products VALUES (1, 'Laptop', 999.99, 1, 1), (2, 'Mouse', 25.50, 1, 2), (3, 'Keyboard', 75.00, 1, 1)",
			"INSERT INTO products VALUES (4, 'Chair', 150.00, 2, 3), (5, 'Desk', 300.00, 2, 3), (6, 'Monitor', 250.00, 1, 2)",
			"INSERT INTO categories VALUES (1, 'Electronics', 1), (2, 'Furniture', 2)",
			"INSERT INTO suppliers VALUES (1, 'TechCorp', 5), (2, 'GadgetInc', 4), (3, 'OfficeSupply', 3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT category_id, GROUP_CONCAT(name ORDER BY (SELECT rating FROM suppliers WHERE suppliers.id = products.supplier_id) DESC, id ASC) FROM products GROUP BY category_id ORDER BY category_id",
				Expected: []sql.Row{{1, "Laptop,Keyboard,Mouse,Monitor"}, {2, "Chair,Desk"}},
			},
			{
				Query:    "SELECT GROUP_CONCAT(name ORDER BY (SELECT COUNT(*) FROM products p2 WHERE p2.price < products.price), id) FROM products",
				Expected: []sql.Row{{"Mouse,Keyboard,Chair,Monitor,Desk,Laptop"}},
			},
			{
				Query:    "SELECT category_id, GROUP_CONCAT(DISTINCT supplier_id ORDER BY (SELECT rating FROM suppliers WHERE suppliers.id = products.supplier_id)) FROM products GROUP BY category_id",
				Expected: []sql.Row{{1, "2,1"}, {2, "3"}},
			},
			{
				Query:    "SELECT GROUP_CONCAT(name ORDER BY (SELECT priority FROM categories WHERE categories.id = products.category_id), price) FROM products",
				Expected: []sql.Row{{"Mouse,Keyboard,Monitor,Laptop,Chair,Desk"}},
			},
			{
				Query:    "SELECT category_id, GROUP_CONCAT(name ORDER BY (SELECT AVG(price) FROM products p2 WHERE p2.category_id = products.category_id) DESC, name) FROM products GROUP BY category_id ORDER BY category_id",
				Expected: []sql.Row{{1, "Keyboard,Laptop,Monitor,Mouse"}, {2, "Chair,Desk"}},
			},
		},
	},
	{
		Name:    "Group Concat Subquery ORDER BY Error Cases",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test_table (id INT PRIMARY KEY, name VARCHAR(50), value INT)",
			"INSERT INTO test_table VALUES (1, 'A', 10), (2, 'B', 20), (3, 'C', 30)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "SELECT GROUP_CONCAT(name ORDER BY (SELECT name, value FROM test_table t2 WHERE t2.id = test_table.id)) FROM test_table",
				ExpectedErr: sql.ErrInvalidOperandColumns,
			},
			{
				Query:       "SELECT GROUP_CONCAT(name ORDER BY (SELECT value FROM test_table)) FROM test_table",
				ExpectedErr: sql.ErrExpectedSingleRow,
			},
		},
	},
	{
		Name:    "Group Concat Subquery ORDER BY Additional Edge Cases",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE complex_test (id INT PRIMARY KEY, name VARCHAR(50), value INT, category VARCHAR(10), created_at DATE)",
			"INSERT INTO complex_test VALUES (1, 'Alpha', 100, 'X', '2023-01-01')",
			"INSERT INTO complex_test VALUES (2, 'Beta', 50, 'Y', '2023-01-15')",
			"INSERT INTO complex_test VALUES (3, 'Gamma', 75, 'X', '2023-02-01')",
			"INSERT INTO complex_test VALUES (4, 'Delta', 25, 'Z', '2023-02-15')",
			"INSERT INTO complex_test VALUES (5, 'Epsilon', 90, 'Y', '2023-03-01')",
		},
		Assertions: []ScriptTestAssertion{
			{
				// Test with subquery returning NULL values
				Query: "SELECT category, GROUP_CONCAT(name ORDER BY (SELECT CASE WHEN complex_test.value > 80 THEN NULL ELSE complex_test.value END), name) FROM complex_test GROUP BY category ORDER BY category",
				Expected: []sql.Row{
					{"X", "Alpha,Gamma"},
					{"Y", "Epsilon,Beta"},
					{"Z", "Delta"},
				},
			},
			{
				// Test with correlated subquery using multiple tables
				Query:    "SELECT GROUP_CONCAT(name ORDER BY (SELECT COUNT(*) FROM complex_test c2 WHERE c2.category = complex_test.category AND c2.value > complex_test.value), name) FROM complex_test",
				Expected: []sql.Row{{"Alpha,Delta,Epsilon,Beta,Gamma"}},
			},
			{
				// Test with subquery using multiple columns errors
				Query:       "SELECT category, GROUP_CONCAT(name ORDER BY (SELECT value, name FROM complex_test c2 WHERE c2.id <= complex_test.id) DESC) FROM complex_test GROUP BY category ORDER BY category",
				ExpectedErr: sql.ErrInvalidOperandColumns,
			},
			{
				// Test with subquery using aggregate functions with HAVING
				Query: "SELECT category, GROUP_CONCAT(name ORDER BY (SELECT AVG(value) FROM complex_test c2 WHERE c2.id <= complex_test.id HAVING AVG(value) > 50) DESC) FROM complex_test GROUP BY category ORDER BY category",
				Expected: []sql.Row{
					{"X", "Alpha,Gamma"},
					{"Y", "Beta,Epsilon"},
					{"Z", "Delta"},
				},
			},
			{
				// Test with DISTINCT and complex subquery
				Query:    "SELECT GROUP_CONCAT(DISTINCT category ORDER BY (SELECT SUM(value) FROM complex_test c2 WHERE c2.category = complex_test.category) DESC SEPARATOR '|') FROM complex_test",
				Expected: []sql.Row{{"X|Y|Z"}},
			},
			{
				// Test with nested subqueries
				Query:    "SELECT GROUP_CONCAT(name ORDER BY (SELECT SUM(value) FROM complex_test c2 WHERE c2.value != (SELECT MIN(value) FROM complex_test c3 where c3.id = complex_test.id))) FROM complex_test;",
				Expected: []sql.Row{{"Alpha,Epsilon,Gamma,Beta,Delta"}},
			},
		},
	},
	{
		Name:    "Group Concat Subquery ORDER BY Performance and Boundary Cases",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE perf_test (id INT PRIMARY KEY, data VARCHAR(10), weight DECIMAL(5,2))",
			"INSERT INTO perf_test VALUES (1, 'A', 1.5), (2, 'B', 2.5), (3, 'C', 0.5), (4, 'D', 3.5), (5, 'E', 2.0)",
		},
		Assertions: []ScriptTestAssertion{
			{
				// Test with subquery returning same value for multiple rows (stability)
				Query:    "SELECT GROUP_CONCAT(data ORDER BY (SELECT 42), id) FROM perf_test",
				Expected: []sql.Row{{"A,B,C,D,E"}},
			},
			{
				// Test with subquery using LIMIT
				Query:    "SELECT GROUP_CONCAT(data ORDER BY (SELECT weight FROM perf_test p2 WHERE p2.id = perf_test.id LIMIT 1)) FROM perf_test",
				Expected: []sql.Row{{"C,A,E,B,D"}},
			},
			{
				// Test with very small decimal differences in ORDER BY subquery
				Query:    "SELECT GROUP_CONCAT(data ORDER BY (SELECT weight + 0.001 * perf_test.id FROM perf_test p2 WHERE p2.id = perf_test.id)) FROM perf_test",
				Expected: []sql.Row{{"C,A,E,B,D"}},
			},
		},
	},
	{
		Name:    "CONVERT USING still converts between incompatible character sets",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 VARCHAR(200)) COLLATE=utf8mb4_0900_ai_ci;",
			"INSERT INTO test VALUES (1, '63273াম'), (2, 'GHD30r'), (3, '8জ্রিয277'), (4, NULL);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT pk, v1, CONVERT(CONVERT(v1 USING latin1) USING utf8mb4) AS round_trip FROM test WHERE v1 <> CONVERT(CONVERT(v1 USING latin1) USING utf8mb4);",
				Expected: []sql.Row{{int64(1), "63273াম", "63273??"}, {int64(3), "8জ্রিয277", "8?????277"}},
			},
		},
	},
	{
		Name:    "ALTER TABLE, ALTER COLUMN SET, DROP DEFAULT",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT NOT NULL DEFAULT 88);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO test (pk) VALUES (1);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT * FROM test;",
				Expected: []sql.Row{{1, 88}},
			},
			{
				Query:    "ALTER TABLE test ALTER v1 SET DEFAULT (CONVERT('42', SIGNED));",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "INSERT INTO test (pk) VALUES (2);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT * FROM test;",
				Expected: []sql.Row{{1, 88}, {2, 42}},
			},
			{
				Query:       "ALTER TABLE test ALTER v2 SET DEFAULT 1;",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query:    "ALTER TABLE test ALTER v1 DROP DEFAULT;",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:       "INSERT INTO test (pk) VALUES (3);",
				ExpectedErr: sql.ErrInsertIntoNonNullableDefaultNullColumn,
			},
			{
				Query:       "ALTER TABLE test ALTER v2 DROP DEFAULT;",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{ // Just confirms that the last INSERT didn't do anything
				Query:    "SELECT * FROM test;",
				Expected: []sql.Row{{1, 88}, {2, 42}},
			},
			{
				Query:    "ALTER TABLE test ALTER v1 SET DEFAULT 100, alter v1 DROP DEFAULT",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:       "INSERT INTO test (pk) VALUES (2);",
				ExpectedErr: sql.ErrInsertIntoNonNullableDefaultNullColumn,
			},
			{
				Query:    "ALTER TABLE test ALTER v1 SET DEFAULT 100, alter v1 SET DEFAULT 200",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:       "ALTER TABLE test DROP COLUMN v1, alter v1 SET DEFAULT 5000",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query: "DESCRIBE test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, ""},
					{"v1", "bigint", "NO", "", "200", ""},
				},
			},
		},
	},
	{
		Name: "Run through some complex queries with DISTINCT and aggregates",
		SetUpScript: []string{
			"CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER)",
			"CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)",
			"INSERT INTO tab1 VALUES(51,14,96)",
			"INSERT INTO tab1 VALUES(85,5,59)",
			"INSERT INTO tab1 VALUES(91,47,68)",
			"INSERT INTO tab2 VALUES(64,77,40)",
			"INSERT INTO tab2 VALUES(75,67,58)",
			"INSERT INTO tab2 VALUES(46,51,23)",
			"CREATE TABLE mytable (pk int, v1 int)",
			"INSERT INTO mytable VALUES(1,1)",
			"INSERT INTO mytable VALUES(1,1)",
			"INSERT INTO mytable VALUES(2,2)",
			"INSERT INTO mytable VALUES(1,2)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT - SUM( DISTINCT - - 71 ) AS col2 FROM tab2 cor0",
				Expected: []sql.Row{{float64(-71)}},
			},
			{
				Query:    "SELECT - SUM ( DISTINCT - - 71 ) AS col2 FROM tab2 cor0",
				Expected: []sql.Row{{float64(-71)}},
			},
			{
				Query:    "SELECT + MAX( DISTINCT ( - col0 ) ) FROM tab1 AS cor0",
				Expected: []sql.Row{{-51}},
			},
			{
				Query:    "SELECT SUM( DISTINCT + col1 ) * - 22 - - ( - COUNT( * ) ) col0 FROM tab1 AS cor0",
				Expected: []sql.Row{{float64(-1455)}},
			},
			{
				Query:    "SELECT MIN (DISTINCT col1) from tab1 GROUP BY col0 ORDER BY col0",
				Expected: []sql.Row{{14}, {5}, {47}},
			},
			{
				Query:    "SELECT SUM (DISTINCT col1) from tab1 GROUP BY col0 ORDER BY col0",
				Expected: []sql.Row{{float64(14)}, {float64(5)}, {float64(47)}},
			},
			{
				Query:    "SELECT pk, SUM(DISTINCT v1), MAX(v1) FROM mytable GROUP BY pk ORDER BY pk",
				Expected: []sql.Row{{int64(1), float64(3), int64(2)}, {int64(2), float64(2), int64(2)}},
			},
			{
				Query:    "SELECT pk, MIN(DISTINCT v1), MAX(DISTINCT v1) FROM mytable GROUP BY pk ORDER BY pk",
				Expected: []sql.Row{{int64(1), int64(1), int64(2)}, {int64(2), int64(2), int64(2)}},
			},
			{
				Query:    "SELECT SUM(DISTINCT pk * v1) from mytable",
				Expected: []sql.Row{{float64(7)}},
			},
			{
				Query:    "SELECT SUM(DISTINCT POWER(v1, 2)) FROM mytable",
				Expected: []sql.Row{{float64(5)}},
			},
			{
				Query:    "SELECT + + 97 FROM tab1 GROUP BY tab1.col1",
				Expected: []sql.Row{{97}, {97}, {97}},
			},
			{
				Query:    "SELECT rand(10) FROM tab1 GROUP BY tab1.col1",
				Expected: []sql.Row{{0.5660920659323543}, {0.5660920659323543}, {0.5660920659323543}},
			},
			{
				Query:    "SELECT ALL - cor0.col0 * + cor0.col0 AS col2 FROM tab1 AS cor0 GROUP BY cor0.col0",
				Expected: []sql.Row{{-2601}, {-7225}, {-8281}},
			},
			{
				Query:    "SELECT cor0.col0 * cor0.col0 + cor0.col0 AS col2 FROM tab1 AS cor0 GROUP BY cor0.col0 order by 1",
				Expected: []sql.Row{{2652}, {7310}, {8372}},
			},
			{
				Query:    "SELECT - floor(cor0.col0) * ceil(cor0.col0) AS col2 FROM tab1 AS cor0 GROUP BY cor0.col0",
				Expected: []sql.Row{{-2601}, {-7225}, {-8281}},
			},
			{
				Query:    "SELECT col0 FROM tab1 AS cor0 GROUP BY cor0.col0",
				Expected: []sql.Row{{51}, {85}, {91}},
			},
			{
				Query:    "SELECT - cor0.col0 FROM tab1 AS cor0 GROUP BY cor0.col0",
				Expected: []sql.Row{{-51}, {-85}, {-91}},
			},
			{
				Query:    "SELECT col0 BETWEEN 2 and 4 from tab1 group by col0",
				Expected: []sql.Row{{false}, {false}, {false}},
			},
			{
				Query:       "SELECT col0, col1 FROM tab1 GROUP by col0;",
				ExpectedErr: analyzererrors.ErrValidationGroupBy,
			},
			{
				Query:       "SELECT col0, floor(col1) FROM tab1 GROUP by col0;",
				ExpectedErr: analyzererrors.ErrValidationGroupBy,
			},
			{
				Query:       "SELECT floor(cor0.col1) * ceil(cor0.col0) AS col2 FROM tab1 AS cor0 GROUP BY cor0.col0",
				ExpectedErr: analyzererrors.ErrValidationGroupBy,
			},
		},
	},
	{
		Name: "Nested Subquery projections (NTC)",
		SetUpScript: []string{
			`CREATE TABLE dcim_site (id char(32) NOT NULL,created date,last_updated datetime,_custom_field_data json NOT NULL,name varchar(100) NOT NULL,_name varchar(100) NOT NULL,slug varchar(100) NOT NULL,facility varchar(50) NOT NULL,asn bigint,time_zone varchar(63) NOT NULL,description varchar(200) NOT NULL,physical_address varchar(200) NOT NULL,shipping_address varchar(200) NOT NULL,latitude decimal(8,6),longitude decimal(9,6),contact_name varchar(50) NOT NULL,contact_phone varchar(20) NOT NULL,contact_email varchar(254) NOT NULL,comments longtext NOT NULL,region_id char(32),status_id char(32),tenant_id char(32),PRIMARY KEY (id),KEY dcim_site_region_id_45210932 (region_id),KEY dcim_site_status_id_e6a50f56 (status_id),KEY dcim_site_tenant_id_15e7df63 (tenant_id),UNIQUE KEY name (name),UNIQUE KEY slug (slug)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;`,
			`CREATE TABLE dcim_rackgroup (id char(32) NOT NULL,created date,last_updated datetime,_custom_field_data json NOT NULL,name varchar(100) NOT NULL,slug varchar(100) NOT NULL,description varchar(200) NOT NULL,lft int unsigned NOT NULL,rght int unsigned NOT NULL,tree_id int unsigned NOT NULL,level int unsigned NOT NULL,parent_id char(32),site_id char(32) NOT NULL,PRIMARY KEY (id),KEY dcim_rackgroup_parent_id_cc315105 (parent_id),KEY dcim_rackgroup_site_id_13520e89 (site_id),KEY dcim_rackgroup_slug_3f4582a7 (slug),KEY dcim_rackgroup_tree_id_9c2ad6f4 (tree_id),UNIQUE KEY site_idname (site_id,name),UNIQUE KEY site_idslug (site_id,slug),CONSTRAINT dcim_rackgroup_parent_id_cc315105_fk_dcim_rackgroup_id FOREIGN KEY (parent_id) REFERENCES dcim_rackgroup (id),CONSTRAINT dcim_rackgroup_site_id_13520e89_fk_dcim_site_id FOREIGN KEY (site_id) REFERENCES dcim_site (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;`,
			`CREATE TABLE dcim_rack (id char(32) NOT NULL,created date,last_updated datetime,_custom_field_data json NOT NULL,name varchar(100) NOT NULL,_name varchar(100) NOT NULL,facility_id varchar(50),serial varchar(50) NOT NULL,asset_tag varchar(50),type varchar(50) NOT NULL,width smallint unsigned NOT NULL,u_height smallint unsigned NOT NULL,desc_units tinyint NOT NULL,outer_width smallint unsigned,outer_depth smallint unsigned,outer_unit varchar(50) NOT NULL,comments longtext NOT NULL,group_id char(32),role_id char(32),site_id char(32) NOT NULL,status_id char(32),tenant_id char(32),PRIMARY KEY (id),UNIQUE KEY asset_tag (asset_tag),KEY dcim_rack_group_id_44e90ea9 (group_id),KEY dcim_rack_role_id_62d6919e (role_id),KEY dcim_rack_site_id_403c7b3a (site_id),KEY dcim_rack_status_id_ee3dee3e (status_id),KEY dcim_rack_tenant_id_7cdf3725 (tenant_id),UNIQUE KEY group_idfacility_id (group_id,facility_id),UNIQUE KEY group_idname (group_id,name),CONSTRAINT dcim_rack_group_id_44e90ea9_fk_dcim_rackgroup_id FOREIGN KEY (group_id) REFERENCES dcim_rackgroup (id),CONSTRAINT dcim_rack_site_id_403c7b3a_fk_dcim_site_id FOREIGN KEY (site_id) REFERENCES dcim_site (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;`,
			`INSERT INTO dcim_site (id, created, last_updated, _custom_field_data, status_id, name, _name, slug, region_id, tenant_id, facility, asn, time_zone, description, physical_address, shipping_address, latitude, longitude, contact_name, contact_phone, contact_email, comments) VALUES ('f0471f313b694d388c8ec39d9590e396', '2021-05-20', '2021-05-20 18:51:46.416695', '{}', NULL, 'Site 1', 'Site 00000001', 'site-1', NULL, NULL, '', NULL, '', '', '', '', NULL, NULL, '', '', '', '')`,
			`INSERT INTO dcim_site (id, created, last_updated, _custom_field_data, status_id, name, _name, slug, region_id, tenant_id, facility, asn, time_zone, description, physical_address, shipping_address, latitude, longitude, contact_name, contact_phone, contact_email, comments) VALUES ('442bab8b517149ab87207e8fb5ba1569', '2021-05-20', '2021-05-20 18:51:47.333720', '{}', NULL, 'Site 2', 'Site 00000002', 'site-2', NULL, NULL, '', NULL, '', '', '', '', NULL, NULL, '', '', '', '')`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('5c107f979f434bf7a7820622f18a5211', '2021-05-20', '2021-05-20 18:51:48.150116', '{}', 'Parent Rack Group 1', 'parent-rack-group-1', 'f0471f313b694d388c8ec39d9590e396', NULL, '', 1, 2, 1, 0)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('6707c20336a2406da6a9d394477f7e8c', '2021-05-20', '2021-05-20 18:51:48.969713', '{}', 'Parent Rack Group 2', 'parent-rack-group-2', '442bab8b517149ab87207e8fb5ba1569', NULL, '', 1, 2, 2, 0)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('6bc0d9b1affe46918b09911359241db6', '2021-05-20', '2021-05-20 18:51:50.566160', '{}', 'Rack Group 1', 'rack-group-1', 'f0471f313b694d388c8ec39d9590e396', '5c107f979f434bf7a7820622f18a5211', '', 2, 3, 1, 1)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('a773cac9dc9842228cdfd8c97a67136e', '2021-05-20', '2021-05-20 18:51:52.126952', '{}', 'Rack Group 2', 'rack-group-2', 'f0471f313b694d388c8ec39d9590e396', '5c107f979f434bf7a7820622f18a5211', '', 4, 5, 1, 1)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('a35a843eb181404bb9da2126c6580977', '2021-05-20', '2021-05-20 18:51:53.706000', '{}', 'Rack Group 3', 'rack-group-3', 'f0471f313b694d388c8ec39d9590e396', '5c107f979f434bf7a7820622f18a5211', '', 6, 7, 1, 1)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('f09a02c95b064533b823e25374f5962a', '2021-05-20', '2021-05-20 18:52:03.037056', '{}', 'Test Rack Group 4', 'test-rack-group-4', '442bab8b517149ab87207e8fb5ba1569', '6707c20336a2406da6a9d394477f7e8c', '', 2, 3, 2, 1)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('ecff5b528c5140d4a58f1b24a1c80ebc', '2021-05-20', '2021-05-20 18:52:05.390373', '{}', 'Test Rack Group 5', 'test-rack-group-5', '442bab8b517149ab87207e8fb5ba1569', '6707c20336a2406da6a9d394477f7e8c', '', 4, 5, 2, 1)`,
			`INSERT INTO dcim_rackgroup (id, created, last_updated, _custom_field_data, name, slug, site_id, parent_id, description, lft, rght, tree_id, level) VALUES ('d31b3772910e4418bdd5725d905e2699', '2021-05-20', '2021-05-20 18:52:07.758547', '{}', 'Test Rack Group 6', 'test-rack-group-6', '442bab8b517149ab87207e8fb5ba1569', '6707c20336a2406da6a9d394477f7e8c', '', 6, 7, 2, 1)`,
			`INSERT INTO dcim_rack (id,created,last_updated,_custom_field_data,name,_name,facility_id,serial,asset_tag,type,width,u_height,desc_units,outer_width,outer_depth,outer_unit,comments,group_id,role_id,site_id,status_id,tenant_id) VALUES ('abc123',  '2021-05-20', '2021-05-20 18:51:48.150116', '{}', "name", "name", "facility", "serial", "assettag", "type", 1, 1, 1, 1, 1, "outer units", "comment", "6bc0d9b1affe46918b09911359241db6", "role", "f0471f313b694d388c8ec39d9590e396", "status", "tenant")`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `SELECT 
                             ((
                               SELECT COUNT(*)
                               FROM dcim_rack
                               WHERE group_id 
                               IN (
                                 SELECT m2.id
                                 FROM dcim_rackgroup m2
                                 WHERE m2.tree_id = dcim_rackgroup.tree_id
                                   AND m2.lft BETWEEN dcim_rackgroup.lft
                                   AND dcim_rackgroup.rght
                               )
                             )) AS rack_count,
                             dcim_rackgroup.id,
                             dcim_rackgroup._custom_field_data,
                             dcim_rackgroup.name,
                             dcim_rackgroup.slug,
                             dcim_rackgroup.site_id,
                             dcim_rackgroup.parent_id,
                             dcim_rackgroup.description,
                             dcim_rackgroup.lft,
                             dcim_rackgroup.rght,
                             dcim_rackgroup.tree_id,
                             dcim_rackgroup.level 
                           FROM dcim_rackgroup
							order by 2 limit 1`,
				Expected: []sql.Row{{1, "5c107f979f434bf7a7820622f18a5211", types.JSONDocument{Val: map[string]interface{}{}}, "Parent Rack Group 1", "parent-rack-group-1", "f0471f313b694d388c8ec39d9590e396", nil, "", uint64(1), uint64(2), uint64(1), uint64(0)}},
			},
		},
	},
	{
		Name: "CREATE TABLE SELECT Queries",
		SetUpScript: []string{
			`CREATE TABLE t1 (pk int PRIMARY KEY, v1 varchar(10))`,
			`INSERT INTO t1 VALUES (1,"1"), (2,"2"), (3,"3")`,
			`CREATE TABLE t2 AS SELECT * FROM t1`,
			// `CREATE TABLE t3(v0 int) AS SELECT pk FROM t1`, // parser problems
			`CREATE TABLE t3 AS SELECT pk FROM t1`,
			`CREATE TABLE t4 AS SELECT pk, v1 FROM t1`,
			`CREATE TABLE t5 SELECT * FROM t1 ORDER BY pk LIMIT 1`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    `SELECT * FROM t2`,
				Expected: []sql.Row{{1, "1"}, {2, "2"}, {3, "3"}},
			},
			{
				Query:    `SELECT * FROM t3`,
				Expected: []sql.Row{{1}, {2}, {3}},
			},
			{
				Query:    `SELECT * FROM t4`,
				Expected: []sql.Row{{1, "1"}, {2, "2"}, {3, "3"}},
			},
			{
				Query:    `SELECT * FROM t5`,
				Expected: []sql.Row{{1, "1"}},
			},
			{
				Query: `CREATE TABLE test SELECT * FROM t1`,
				Expected: []sql.Row{sql.Row{types.OkResult{
					RowsAffected: 3,
					InsertID:     0,
					Info:         nil,
				}}},
			},
		},
	},
	{
		Name:    "unix_timestamp function usage",
		Dialect: "mysql",
		SetUpScript: []string{
			// NOTE: session time zone needs to be set as UNIX_TIMESTAMP function depends on it and converts the final result
			"SET @@SESSION.time_zone = 'UTC';",
			"CREATE TABLE `datetime_table` (   `i` bigint NOT NULL,   `date_col` date,   `datetime_col` datetime,   `timestamp_col` timestamp,   `time_col` time(6),   PRIMARY KEY (`i`) )",
			`insert into datetime_table values
    (1, '2019-12-31T12:00:00Z', '2020-01-01T12:00:00Z', '2020-01-02T12:00:00Z', '03:10:0'),
    (2, '2020-01-03T12:00:00Z', '2020-01-04T12:00:00Z', '2020-01-05T12:00:00Z', '04:00:44'),
    (3, '2020-01-07T00:00:00Z', '2020-01-07T12:00:00Z', '2020-01-07T12:00:01Z', '15:00:00.005000')`,
			`create index datetime_table_d on datetime_table (date_col)`,
			`create index datetime_table_dt on datetime_table (datetime_col)`,
			`create index datetime_table_ts on datetime_table (timestamp_col)`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT unix_timestamp(timestamp_col) div 60 * 60 as timestamp_col, avg(i) from datetime_table group by 1 order by unix_timestamp(timestamp_col) div 60 * 60",
				Expected: []sql.Row{
					{int64(1577966400), 1.0},
					{int64(1578225600), 2.0},
					{int64(1578398400), 3.0}},
			},
		},
	},
	{
		Name:    "from_unixtime",
		Dialect: "mysql",
		Assertions: []ScriptTestAssertion{
			// null parameter
			{
				Query:    "select from_unixtime(null)",
				Expected: []sql.Row{{nil}},
			},
			{
				Query:    "select from_unixtime(1, null)",
				Expected: []sql.Row{{nil}},
			},
			// out of range
			{
				Query:    "select from_unixtime(-1)",
				Expected: []sql.Row{{nil}},
			},
			{
				Query:    "select from_unixtime(32536771200)",
				Expected: []sql.Row{{nil}},
			},
			// in +8:00
			{
				Query:    "set @@session.time_zone='+08:00'",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "select from_unixtime(1)",
				Expected: []sql.Row{{time.Unix(1, 0).Add(time.Hour * 8).In(time.UTC)}},
			},
			{
				Query:    "select from_unixtime(32536771199)",
				Expected: []sql.Row{{time.Unix(32536771199, 0).Add(time.Hour * 8).In(time.UTC)}},
			},
			{
				Query:    "SELECT FROM_UNIXTIME(1,'%Y %D %M %H:%i:%s %x')",
				Expected: []sql.Row{{"1970 1st January 08:00:01 1970"}},
			},
			// in utc
			{
				Query:    "set @@session.time_zone='UTC'",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "select from_unixtime(1)",
				Expected: []sql.Row{{time.Unix(1, 0).In(time.UTC)}},
			},
			{
				Query:    "select from_unixtime(32536771199)",
				Expected: []sql.Row{{time.Unix(32536771199, 0).In(time.UTC)}},
			},
			{
				Query:    "SELECT FROM_UNIXTIME(1,'%Y %D %M %H:%i:%s %x')",
				Expected: []sql.Row{{"1970 1st January 00:00:01 1970"}},
			},
		},
	},
	{
		Name:    "unix_timestamp with non UTC timezone",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET @@SESSION.time_zone = 'UTC';",
			"CREATE TABLE `datetime_table` (   `i` bigint NOT NULL,   `datetime_col` datetime,   `timestamp_col` timestamp,   PRIMARY KEY (`i`) )",
			"insert into datetime_table(i,datetime_col,timestamp_col)values(1, '1970-01-02 00:00:00', '1970-01-02 00:00:00')",
			"SET @@SESSION.time_zone = 'Asia/Shanghai';", // +8:00
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT unix_timestamp(timestamp_col), unix_timestamp(datetime_col) from datetime_table",
				Expected: []sql.Row{
					{"86400", "57600"},
				},
			},
		},
	},
	{
		Name:    "Issue #499", // https://github.com/dolthub/go-mysql-server/issues/499
		Dialect: "mysql",
		SetUpScript: []string{
			"SET @@SESSION.time_zone = 'UTC';",
			"CREATE TABLE test (time TIMESTAMP, value DOUBLE);",
			`INSERT INTO test VALUES 
			("2021-07-04 10:00:00", 1.0),
			("2021-07-03 10:00:00", 2.0),
			("2021-07-02 10:00:00", 3.0),
			("2021-07-01 10:00:00", 4.0);`,
		},
		Assertions: []ScriptTestAssertion{
			{
				// In the original, reported issue, the order by clause did not qualify the table name
				// for `test.time`. When there is ambiguity between a column name and an expression
				// alias name in the order by clause, MySQL choose the alias; however, if the reference
				// is used in a function call, MySQL instead seems to resolve to the column name.
				// Until we determine the exact rule for this behavior, we've qualified the reference
				// in the order by clause to ensure it selects the table column and not the alias.
				// TODO: Waiting to hear back from MySQL on whether this is intended behavior or not:
				//       https://bugs.mysql.com/bug.php?id=109020
				Query: `SELECT UNIX_TIMESTAMP(time) DIV 60 * 60 AS "time", avg(value) AS "value"
				FROM test GROUP BY 1 ORDER BY UNIX_TIMESTAMP(test.time) DIV 60 * 60`,
				Expected: []sql.Row{
					{int64(1625133600), 4.0},
					{int64(1625220000), 3.0},
					{int64(1625306400), 2.0},
					{int64(1625392800), 1.0},
				},
			},
		},
		// todo(max): fix arithmatic on bindvar typing
		SkipPrepared: true,
	},
	{
		Name: "Slightly more complex example for the Exists Clause",
		SetUpScript: []string{
			"create table store(store_id int, item_id int, primary key (store_id, item_id))",
			"create table items(item_id int primary key, price int)",
			"insert into store values (0, 1), (0,2),(0,3),(1,2),(1,4),(2,1)",
			"insert into items values (1, 10), (2, 20), (3, 30),(4,40)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT * from store WHERE EXISTS (SELECT price from items where price > 10 and store.item_id = items.item_id)",
				Expected: []sql.Row{{0, 2}, {0, 3}, {1, 2}, {1, 4}},
			},
		},
	},
	{
		Name:    "Simple Update Join test that manipulates two tables",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (pk int primary key);",
			`CREATE TABLE test2 (pk int primary key, val int);`,
			`INSERT into test values (0),(1),(2),(3)`,
			`INSERT into test2 values (0, 0),(1, 1),(2, 2),(3, 3)`,
			`CREATE TABLE test3(k int, val int, primary key (k, val))`,
			`INSERT into test3 values (1,2),(1,3),(1,4)`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `update test2 inner join (select * from test3 order by val) as t3 on test2.pk = t3.k SET test2.val=t3.val`,
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, Info: plan.UpdateInfo{
					Matched:  1,
					Updated:  1,
					Warnings: 0,
				}}}},
			},
			{
				Query: "SELECT val FROM test2 where pk = 1",
				Expected: []sql.Row{
					{2},
				},
			},
			{
				Query: `update test inner join test2 on test.pk = test2.pk SET test.pk=test.pk*10, test2.pk = test2.pk * 4 where test.pk < 10;`,
				Expected: []sql.Row{{types.OkResult{RowsAffected: 6, Info: plan.UpdateInfo{
					Matched:  8,
					Updated:  6,
					Warnings: 0,
				}}}},
			},
			{
				Query: "SELECT * FROM test",
				Expected: []sql.Row{
					{0},
					{10},
					{20},
					{30},
				},
			},
			{
				Query: "SELECT * FROM test2",
				Expected: []sql.Row{
					{0, 0},
					{4, 2},
					{8, 2},
					{12, 3},
				},
			},
			{
				Query: `update test2 inner join (select * from test3 order by val) as t3 on false SET test2.val=t3.val`,
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0, Info: plan.UpdateInfo{
					Matched:  0,
					Updated:  0,
					Warnings: 0,
				}}}},
			},
		},
	},
	{
		Name: "Partial indexes are used and return the expected result",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT, v2 BIGINT, v3 BIGINT, INDEX vx (v3, v2, v1));",
			"INSERT INTO test VALUES (1,2,3,4), (2,3,4,5), (3,4,5,6), (4,5,6,7), (5,6,7,8);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT * FROM test WHERE v3 = 4;",
				Expected: []sql.Row{{1, 2, 3, 4}},
			},
			{
				Query:    "SELECT * FROM test WHERE v3 = 8 AND v2 = 7;",
				Expected: []sql.Row{{5, 6, 7, 8}},
			},
			{
				Query:    "SELECT * FROM test WHERE v3 >= 6 AND v2 >= 6;",
				Expected: []sql.Row{{4, 5, 6, 7}, {5, 6, 7, 8}},
			},
			{
				Query:    "SELECT * FROM test WHERE v3 = 7 AND v2 >= 6;",
				Expected: []sql.Row{{4, 5, 6, 7}},
			},
		},
	},
	{
		Name: "Multiple indexes on the same columns in a different order",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT, v2 BIGINT, v3 BIGINT, INDEX v123 (v1, v2, v3), INDEX v321 (v3, v2, v1), INDEX v132 (v1, v3, v2));",
			"INSERT INTO test VALUES (1,2,3,4), (2,3,4,5), (3,4,5,6), (4,5,6,7), (5,6,7,8);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT * FROM test WHERE v1 = 2 AND v2 > 1;",
				Expected: []sql.Row{{1, 2, 3, 4}},
			},
			{
				Query:    "SELECT * FROM test WHERE v2 = 4 AND v3 > 1;",
				Expected: []sql.Row{{2, 3, 4, 5}},
			},
			{
				Query:    "SELECT * FROM test WHERE v3 = 6 AND v1 > 1;",
				Expected: []sql.Row{{3, 4, 5, 6}},
			},
			{
				Query:    "SELECT * FROM test WHERE v1 = 5 AND v3 <= 10 AND v2 >= 1;",
				Expected: []sql.Row{{4, 5, 6, 7}},
			},
		},
	},
	{
		Name: "Ensure proper DECIMAL support (found by fuzzer)",
		SetUpScript: []string{
			"CREATE TABLE `GzaKtwgIya` (`K7t5WY` DECIMAL(64,5), `qBjVrN` VARBINARY(1000), `PvqQtc` SET('c3q6y','kxMqhfkK','XlRI8','dF0N63H','hMPjt0KXRLwCGRr','27fi2s','1FSJ','NcPzIN','Za18lbIgxmZ','on4BKKXykVTbJ','WBfO','RMNG','Sd7','FDzbEO','cLRdLOj1y','syo4','Ul','jfsfDCx6s','yEW3','JyQcWFDl'), `1kv7el` FLOAT, `Y3vfRG` BLOB, `Ijq8CK` TINYTEXT, `tzeStN` MEDIUMINT, `Ak83FQ` BINARY(64), `8Nbp3L` DOUBLE, PRIMARY KEY (`K7t5WY`));",
			"REPLACE INTO `GzaKtwgIya` VALUES ('58567047399981325523662211357420045483361289734772861386428.89028','bvo5~Tt8%kMW2nm2!8HghaeulI6!pMadE+j-J2LeU1O1*-#@Lm8Ibh00bTYiA*H1Q8P1_kQq 24Rrd4@HeF%#7#C#U7%mqOMrQ0%!HVrGV1li.XyYa:7#3V^DtAMDTQ9 cY=07T4|DStrwy4.MAQxOG#1d#fcq+7675$y0e96-2@8-WlQ^p|%E!a^TV!Yj2_eqZZys1z:883l5I%zAT:i56K^T!cx#us $60Tb#gH$1#$P.709E#VrH9FbQ5QZK2hZUH!qUa4Xl8*I*0fT~oAha$8jU5AoWs+Uv!~:14Yq%pLXpP9RlZ:Gd1g|*$Qa.9*^K~YlYWVaxwY~_g6zOMpU$YijT+!_*m3=||cMNn#uN0!!OyCg~GTQlJ11+#@Ohqc7b#2|Jp2Aei56GOmq^I=7cQ=sQh~V.D^HzwK5~4E$QzFXfWNVN5J_w2b4dkR~bB~7F%=@R@9qE~e:-_RnoJcOLfBS@0:*hTIP$5ui|5Ea-l+qU4nx98X6rV2bLBxn8am@p~:xLF#T^_9kJVN76q^18=i *FJo.v-xA2GP==^C^Jz3yBF0OY4bIxC59Y#6G=$w:xh71kMxBcYJKf3+$Ci_uWx0P*AfFNne0_1E0Lwv#3J8vm:. 8Wo~F3VT:@w.t@w .JZz$bok9Tls7RGo=~4 Y$~iELr$s@53YuTPM8oqu!x*1%GswpJR=0K#qs00nW-1MqEUc:0wZv#X4qY^pzVDb:!:!yDhjhh+KIT%2%w@+t8c!f~o!%EnwBIr_OyzL6e1$-R8n0nWPU.toODd*|fW3H$9ZLc9!dMS:QfjI0M$nK 8aGvUVP@9kS~W#Y=Q%=37$@pAUkDTXkJo~-DRvCG6phPp*Xji@9|AEODHi+-6p%X4YM5Y3WasPHcZQ8QgTwi9 N=2RQD_MtVU~0J~3SAx*HrMlKvCPTswZq#q_96ny_A@7g!E2jyaxWFJD:C233onBdchW$WdAc.LZdZHYDR^uwZb9B9p-q.BkD1I',608583,'-7.276514330627342e-28','FN3O_E:$ 5S40T7^Vu1g!Ktn^N|4RE!9GnZiW5dG:%SJb5|SNuuI.d2^qnMY.Xn*_fRfk Eo7OhqY8OZ~pA0^ !2P.uN~r@pZ2!A0+4b*%nxO.tm%S6=$CZ9+c1zu-p $b:7:fOkC%@E3951st@2Q93~8hj:ZGeJ6S@nw-TAG+^lad37aB#xN*rD^9TO0|hleA#.Nh28S2PB72L*TxD0$|XE3S5eVVmbI*pkzE~lPecopX1fUyFj#LC+%~pjmab7^ Kdd4B%8I!ohOCQV.oiw++N|#W2=D4:_sK0@~kTTeNA8_+FMKRwro.M0| LdKHf-McKm0Z-R9+H%!9r l6%7UEB50yNH-ld%eW8!f=LKgZLc*TuTP2DA_o0izvzZokNp3ShR+PA7Fk* 1RcSt5KXe+8tLc+WGP','3RvfN2N.Q1tIffE965#2r=u_-4!u:9w!F1p7+mSsO8ckio|ib 1t@~GtgUkJX',1858932,'DJMaQcI=vS-Jk2L#^2N8qZcRpMJ2Ga!30A+@I!+35d-9bwVEVi5-~i.a%!KdoF5h','1.0354401044541863e+255');",
			"INSERT INTO `GzaKtwgIya` VALUES ('91198031969464085142628031466155813748261645250257051732159.65596','96Lu=focmodq4otVAUN6TD-F$@k^4443higo=KH!1WBDH9|vpEGdO* 1uF6yWjT4:7G|altXnWSv+d:c8Km8vL!b%-nuB8mAxO9E|a5N5#v@z!ij5ifeIEoZGXrhBJl.m*Rx-@%g~t:y$3Pp3Q7Bd3y$=YG%6yibqXWO9$SS+g=*6QzdSCzuR~@v!:.ATye0A@y~DG=uq!PaZd6wN7.2S Aq868-RN3RM61V#N+Qywqo=%iYV*554@h6GPKZ| pmNwQw=PywuyBhr*MHAOXV+u9_-#imKI-wT4gEcA1~lGg1cfL2IvhkwOXRhrjAx-8+R3#4!Ai J6SYP|YUuuGalJ_N8k_8K^~h!JyiH$0JbGQ4AOxO3-eW=BaopOd8FF1.cfFMK!tXR ^I15g:npOuZZO$Vq3yQ4bl4s$E9:t2^.4f.:I4_@u9_UI1ApBthJZNiv~o#*uhs9K@ufZ1YPJQY-pMj$v-lQ2#%=Uu!iEAO3%vQ^5YITKcWRk~$kd1H#F675r@P5#M%*F_xP3Js7$YuEC4YuQjZ A74tMw:KwQ8dR:k_ Sa85G~42-K3%:jk5G9csC@iW3nY|@-:_dg~5@J!FWF5F+nyBgz4fDpdkdk9^:_.t$A3W-C@^Ax.~o|Rq96_i%HeG*7jBjOGhY-e1k@aD@WW.@GmpGAI|T-84gZFG3BU9@#9lpL|U2YCEA.BEA%sxDZ Kw:n+d$Y!SZw0Iml$Bdtyr:02Np=DZpiI%$N9*U=%Jq#$P5BI60WOTK+UynVx9Dd**5q8y9^v+I|PPa#_2XheV5YQU.ONdQQNJxsiRaEl!*=xv4bTWj1wBH#_-eM3T',490529,'-8.419238802182018e+25','|WD!NpWJOfN+_Au 1y!|XF8l38#%%R5%$TRUEaFt%4ywKQ8 O1LD-3qRDrnHAXboH~0uivbo87f+V%=q9~Mvz1EIxsU!whSmPqtb9r*11346R_@L+H#@@Z9H-Dc6j%.D0o##m@B9o7jO#~N81ACI|f#J3z4dho:jc54Xws$8r%cxuov^1$w_58Fv2*.8qbAW$TF153A:8wwj4YIhkd#^Q7 |g7I0iQG0p+yE64rk!Pu!SA-z=ELtLNOCJBk_4!lV$izn%sB6JwM+uq~ 49I7','v|eUA_h2@%t~bn26ci8Ngjm@Lk*G=l2MhxhceV2V|ka#c',8150267,'nX-=1Q$3riw_jlukGuHmjodT_Y_SM$xRbEt$%$%hlIUF1+GpRp~U6JvRX^: k@n#','7.956726808353253e+267');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "DELETE FROM `GzaKtwgIya` WHERE `K7t5WY` = '58567047399981325523662211357420045483361289734772861386428.89028';",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT COUNT(*) FROM GzaKtwgIya",
				Expected: []sql.Row{{1}},
			},
		},
	},
	{
		Name:    "Ensure scale is not rounded when inserting to DECIMAL type through float64",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table test (number decimal(40,16));",
			"insert into test values ('11981.5923291839784651');",
			"create table small_test (n decimal(3,2));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT COUNT(*) FROM test WHERE number = CONVERT('11981.5923291839784651', DECIMAL)",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "INSERT INTO test VALUES (11981.5923291839784651);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT COUNT(*) FROM test WHERE number = CONVERT('11981.5923291839784651', DECIMAL)",
				Expected: []sql.Row{{2}},
			},
			{
				Query:    "INSERT INTO test VALUES (119815923291839784651.11981592329183978465111981592329183978465144);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT COUNT(*) FROM test WHERE number = CONVERT('119815923291839784651.1198159232918398', DECIMAL)",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "INSERT INTO test VALUES (1.1981592329183978465111981592329183978465111981592329183978465144);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT COUNT(*) FROM test WHERE number = CONVERT('1.1981592329183978', DECIMAL)",
				Expected: []sql.Row{{1}},
			},
			{
				Query:    "INSERT INTO test VALUES (1.1981592329183978545111981592329183978465111981592329183978465144);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT COUNT(*) FROM test WHERE number = CONVERT('1.1981592329183979', DECIMAL)",
				Expected: []sql.Row{{1}},
			},
			{
				Query:       "INSERT INTO small_test VALUES (12.1);",
				ExpectedErr: types.ErrConvertToDecimalLimit,
			},
		},
	},
	{
		Name: "JOIN on non-index-prefix columns do not panic (Dolt Issue #2366)",
		SetUpScript: []string{
			"CREATE TABLE `player_season_stat_totals` (`player_id` int NOT NULL, `team_id` int NOT NULL, `season_id` int NOT NULL, `minutes` int, `games_started` int, `games_played` int, `2pm` int, `2pa` int, `3pm` int, `3pa` int, `ftm` int, `fta` int, `ast` int, `stl` int, `blk` int, `tov` int, `pts` int, `orb` int, `drb` int, `trb` int, `pf` int, `season_type_id` int NOT NULL, `league_id` int NOT NULL DEFAULT 0, PRIMARY KEY (`player_id`,`team_id`,`season_id`,`season_type_id`,`league_id`));",
			"CREATE TABLE `team_seasons` (`team_id` int NOT NULL, `league_id` int NOT NULL, `season_id` int NOT NULL, `prefix` varchar(100), `nickname` varchar(100), `abbreviation` varchar(100), `city` varchar(100), `state` varchar(100), `country` varchar(100), PRIMARY KEY (`team_id`,`league_id`,`season_id`));",
			"INSERT INTO player_season_stat_totals VALUES (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1);",
			"INSERT INTO team_seasons VALUES (1,1,1,'','','','','','');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT stats.* FROM player_season_stat_totals stats LEFT JOIN team_seasons ON team_seasons.team_id = stats.team_id AND team_seasons.season_id = stats.season_id;",
				Expected: []sql.Row{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}},
			},
		},
	},
	{
		Name: "Show create table with various keys and constraints",
		SetUpScript: []string{
			"create table t1(a int primary key, b varchar(10) not null default 'abc')",
			"alter table t1 add constraint ck1 check (b like '%abc%')",
			"create index t1b on t1(b)",
			"create table t2(c int primary key, d varchar(10))",
			"alter table t2 add constraint t2du unique (d)",
			"alter table t2 add constraint fk1 foreign key (d) references t1 (b)",
			"create table t3 (a int, b varchar(100), c datetime(6), primary key (b,a))",
			"create table t4 (a int default (floor(1)), b int default (coalesce(a, 10)))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t1",
				Expected: []sql.Row{
					{"t1", "CREATE TABLE `t1` (\n" +
						"  `a` int NOT NULL,\n" +
						"  `b` varchar(10) NOT NULL DEFAULT 'abc',\n" +
						"  PRIMARY KEY (`a`),\n" +
						"  KEY `t1b` (`b`),\n" +
						"  CONSTRAINT `ck1` CHECK (`b` LIKE '%abc%')\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "show create table t2",
				Expected: []sql.Row{
					{"t2", "CREATE TABLE `t2` (\n" +
						"  `c` int NOT NULL,\n" +
						"  `d` varchar(10),\n" +
						"  PRIMARY KEY (`c`),\n" +
						"  UNIQUE KEY `t2du` (`d`),\n" +
						"  CONSTRAINT `fk1` FOREIGN KEY (`d`) REFERENCES `t1` (`b`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "show create table t3",
				Expected: []sql.Row{
					{"t3", "CREATE TABLE `t3` (\n" +
						"  `a` int NOT NULL,\n" +
						"  `b` varchar(100) NOT NULL,\n" +
						"  `c` datetime(6),\n" +
						"  PRIMARY KEY (`b`,`a`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "show create table t4",
				Expected: []sql.Row{
					{"t4", "CREATE TABLE `t4` (\n" +
						"  `a` int DEFAULT (floor(1)),\n" +
						"  `b` int DEFAULT (coalesce(`a`,10))\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},
	{
		Name: "show create table with duplicate primary key",
		SetUpScript: []string{
			"create table t (i int primary key)",
			"create index notpk on t(i)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t",
				Expected: []sql.Row{
					{"t", "CREATE TABLE `t` (\n" +
						"  `i` int NOT NULL,\n" +
						"  PRIMARY KEY (`i`),\n" +
						"  KEY `notpk` (`i`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query:          "create index `primary` on t(i)",
				ExpectedErrStr: "invalid index name 'primary'",
			},
		},
	},
	{
		Name: "table with defaults, insert with on duplicate key update",
		SetUpScript: []string{
			"create table t (a int primary key, b int default 100);",
			"insert into t values (1, 1), (2, 2)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "insert into t values (1, 10) on duplicate key update b = 10",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
		},
	},
	{
		Name: "delete from table with misordered pks",
		SetUpScript: []string{
			"create table a (x int, y int, z int, primary key (z,x))",
			"insert into a values (0,1,2), (3,4,5)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT count(*) FROM a where x = 0",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query:    "delete from a where x = 0",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SELECT * FROM a where x = 0",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "recreate primary key rebuilds secondary indexes",
		SetUpScript: []string{
			"create table a (x int, y int, z int, primary key (x,y,z), index idx1 (y))",
			"insert into a values (1,2,3), (4,5,6), (7,8,9)",
			"alter table a drop primary key",
			"alter table a add primary key (y,z,x)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "delete from a where y = 2",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "delete from a where y = 2",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "select * from a where y = 2",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from a where y = 5",
				Expected: []sql.Row{{4, 5, 6}},
			},
		},
	},
	{
		Name: "Handle hex number to binary conversion",
		SetUpScript: []string{
			"CREATE TABLE hex_nums1 (pk BIGINT PRIMARY KEY, v1 INT, v2 BIGINT UNSIGNED, v3 DOUBLE, v4 BINARY(32));",
			"INSERT INTO hex_nums1 values (1, 0x7ED0599B, 0x765a8ce4ce74b187, 0xF753AD20B0C4, 0x148aa875c3cdb9af8919493926a3d7c6862fec7f330152f400c0aecb4467508a);",
			"CREATE TABLE hex_nums2 (pk BIGINT PRIMARY KEY, v1 VARBINARY(255), v2 BLOB);",
			"INSERT INTO hex_nums2 values (1, 0x765a8ce4ce74b187, 0x148aa875c3cdb9af8919493926a3d7c6862fec7f330152f400c0aecb4467508a);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT v1, v2, v3, hex(v4) FROM hex_nums1;",
				Expected: []sql.Row{{2127583643, uint64(8528283758723641735), float64(271938758947012), "148AA875C3CDB9AF8919493926A3D7C6862FEC7F330152F400C0AECB4467508A"}},
			},
			{
				Query:    "SELECT hex(v1), hex(v2), hex(v3), hex(v4) FROM hex_nums1;",
				Expected: []sql.Row{{"7ED0599B", "765A8CE4CE74B187", "F753AD20B0C4", "148AA875C3CDB9AF8919493926A3D7C6862FEC7F330152F400C0AECB4467508A"}},
			},
			{
				Query:    "SELECT hex(v1), hex(v2) FROM hex_nums2;",
				Expected: []sql.Row{{"765A8CE4CE74B187", "148AA875C3CDB9AF8919493926A3D7C6862FEC7F330152F400C0AECB4467508A"}},
			},
		},
	},
	{
		Name:    "Multialter DDL with ADD/DROP Primary Key",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t(pk int primary key, v1 int)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "ALTER TABLE t ADD COLUMN (v2 int), drop primary key, add primary key (v2)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				Query:       "ALTER TABLE t ADD COLUMN (v3 int), drop primary key, add primary key (notacolumn)",
				ExpectedErr: sql.ErrKeyColumnDoesNotExist,
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "NO", "PRI", nil, ""},
				},
			},
		},
	},
	{
		Name:    "Multialter DDL with ADD/DROP INDEX",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t(pk int primary key, v1 int)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "ALTER TABLE t DROP COLUMN v1, ADD INDEX myidx (v1)",
				ExpectedErr: sql.ErrKeyColumnDoesNotExist,
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""}, // should not be dropped
				},
			},
			{
				Query:    "ALTER TABLE t ADD COLUMN (v2 int), ADD INDEX myidx (v2)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "YES", "MUL", nil, ""},
				},
			},
			{
				Query:       "ALTER TABLE t ADD COLUMN (v3 int), DROP INDEX notanindex",
				ExpectedErr: sql.ErrCantDropFieldOrKey,
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "YES", "MUL", nil, ""},
				},
			},
			{
				Query:       "ALTER TABLE t ADD COLUMN (v4 int), ADD INDEX myidx (notacolumn)",
				ExpectedErr: sql.ErrKeyColumnDoesNotExist,
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "YES", "MUL", nil, ""},
				},
			},
			{
				Query:       "ALTER TABLE t ADD COLUMN (v4 int), ADD INDEX myidx2 (v4), DROP INDEX notanindex;",
				ExpectedErr: sql.ErrCantDropFieldOrKey,
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "YES", "MUL", nil, ""},
				},
			},
			{
				Query:    "ALTER TABLE t ADD COLUMN (v4 int), ADD INDEX myidx2 (v4)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "YES", "MUL", nil, ""},
					{"v4", "int", "YES", "MUL", nil, ""},
				},
			},
			{
				Query:    "ALTER TABLE t ADD COLUMN (v5 int), RENAME INDEX myidx2 TO myidx3",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "ALTER TABLE t DROP INDEX myidx, ADD INDEX v5idx (v5)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "YES", "", nil, ""},
					{"v4", "int", "YES", "MUL", nil, ""},
					{"v5", "int", "YES", "MUL", nil, ""},
				},
			},
		},
	},
	{
		Name: "alter json column default; from scorewarrior: https://github.com/dolthub/dolt/issues/4543",
		SetUpScript: []string{
			"CREATE TABLE test (i int default 999, j json);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "alter table test alter column j set default ('[]');",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:   "show create table test",
				Dialect: "mysql",
				Expected: []sql.Row{
					{"test", "CREATE TABLE `test` (\n  `i` int DEFAULT '999',\n  `j` json DEFAULT ('[]')\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},
	{
		Name:    "ALTER TABLE MULTI ADD/DROP COLUMN",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 BIGINT NOT NULL DEFAULT 88);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO test (pk) VALUES (1);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "ALTER TABLE test DROP COLUMN v1, ADD COLUMN v2 INT NOT NULL DEFAULT 100",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, ""},
					{"v2", "int", "NO", "", "100", ""},
				},
			},
			{
				Query:    "ALTER TABLE TEST MODIFY COLUMN pk BIGINT AUTO_INCREMENT, AUTO_INCREMENT = 100",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "INSERT INTO test (v2) values (11)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1, InsertID: 100}}},
			},
			{
				Query:    "SELECT * from test where pk = 100",
				Expected: []sql.Row{{100, 11}},
			},
			{
				Query:       "ALTER TABLE test DROP COLUMN v2, ADD COLUMN v3 int NOT NULL after v2",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, "auto_increment"},
					{"v2", "int", "NO", "", "100", ""},
				},
			},
			{
				Query:       "ALTER TABLE test DROP COLUMN v2, RENAME COLUMN v2 to v3",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, "auto_increment"},
					{"v2", "int", "NO", "", "100", ""},
				},
			},
			{
				Query:       "ALTER TABLE test RENAME COLUMN v2 to v3, DROP COLUMN v2",
				ExpectedErr: sql.ErrTableColumnNotFound,
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, "auto_increment"},
					{"v2", "int", "NO", "", "100", ""},
				},
			},
			{
				Query:    "ALTER TABLE test ADD COLUMN (v3 int NOT NULL), add column (v4 int), drop column v2, add column (v5 int NOT NULL)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, "auto_increment"},
					{"v3", "int", "NO", "", nil, ""},
					{"v4", "int", "YES", "", nil, ""},
					{"v5", "int", "NO", "", nil, ""},
				},
			},
			{
				Query:    "ALTER TABLE test ADD COLUMN (v6 int not null), RENAME COLUMN v5 TO mycol, DROP COLUMN v4, ADD COLUMN (v7 int);",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"pk", "bigint", "NO", "PRI", nil, "auto_increment"},
					{"v3", "int", "NO", "", nil, ""},
					{"mycol", "int", "NO", "", nil, ""},
					{"v6", "int", "NO", "", nil, ""},
					{"v7", "int", "YES", "", nil, ""},
				},
			},
			// TODO: Does not include tests with column renames and defaults.
		},
	},
	{
		Name:    "describe and show columns with various keys and constraints",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t1 (i int not null, unique key (i));",
			"create table t2 (i int not null, j int not null, unique key (j), unique key(i));",
			"create table t3 (i int not null, j int, unique key (i, j));",
			"create table t4 (i int not null, j int primary key, unique key (i));",
			"create table t5 (i int not null, j int not null, unique key (j, i), unique key (i));",
			"create table t6 (i int not null, j int not null, unique key (i), unique key (j, i));",
			"create table t7 (pk int primary key, i int, j int not null, unique key (i), unique key (j, i));",
			"create table t8 (pk int primary key, i int, j int, k int, unique key (i, j, k), unique key (i), unique key (j), unique key(k));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t1;",
				Expected: []sql.Row{
					{"t1", "CREATE TABLE `t1` (\n" +
						"  `i` int NOT NULL,\n" +
						"  UNIQUE KEY `i` (`i`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "describe t1;",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				Query: "show columns from t1;",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				Skip:  true, // supposed to be the first index defined, not in order of columns
				Query: "describe t2;",
				Expected: []sql.Row{
					{"i", "int", "NO", "UNI", nil, ""},
					{"j", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				Query: "describe t3;",
				Expected: []sql.Row{
					{"i", "int", "NO", "MUL", nil, ""},
					{"j", "int", "YES", "", nil, ""},
				},
			},
			{
				Query: "describe t4;",
				Expected: []sql.Row{
					{"i", "int", "NO", "UNI", nil, ""},
					{"j", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				// MySQL reads indexes in the order that they were created, while we sort by idx name
				// https://github.com/dolthub/dolt/issues/2289
				Skip:  true,
				Query: "describe t5;",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"j", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				Query: "describe t6;",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"j", "int", "NO", "MUL", nil, ""},
				},
			},
			{
				Query: "describe t7;",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"i", "int", "YES", "UNI", nil, ""},
					{"j", "int", "NO", "MUL", nil, ""},
				},
			},
			{
				Skip:  true, // for some reason MUL takes priority over UNI for i
				Query: "describe t8;",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"i", "int", "YES", "MUL", nil, ""},
					{"j", "int", "YES", "UNI", nil, ""},
					{"k", "int", "YES", "UNI", nil, ""},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/3065
		Name: "join index lookups do not handle filters",
		SetUpScript: []string{
			"create table a (x int primary key)",
			"create table b (y int primary key, x int, index idx_x(x))",
			"create table c (z int primary key, x int, y int, index idx_x(x))",
			"insert into a values (0),(1),(2),(3)",
			"insert into b values (0,1), (1,1), (2,2), (3,2)",
			"insert into c values (0,1,0), (1,1,0), (2,2,1), (3,2,1)",
		},
		Query: "select a.* from a join b on a.x = b.x join c where c.x = a.x and b.x = 1",
		Expected: []sql.Row{
			{1},
			{1},
			{1},
			{1},
		},
	},
	{
		Name:    "failed conversion shows warning",
		Dialect: "mysql",
		Assertions: []ScriptTestAssertion{
			{
				Query:                           "SELECT CONVERT('10000-12-31 23:59:59', DATETIME)",
				ExpectedWarning:                 1292,
				ExpectedWarningsCount:           1,
				ExpectedWarningMessageSubstring: "Incorrect datetime value: 10000-12-31 23:59:59",
				SkipResultsCheck:                true,
			},
			{
				Query:                           "SELECT CONVERT('this is not a datetime', DATETIME)",
				ExpectedWarning:                 1292,
				ExpectedWarningsCount:           1,
				ExpectedWarningMessageSubstring: "Incorrect datetime value: this is not a datetime",
				SkipResultsCheck:                true,
			},
			{
				Query:                           "SELECT CAST('this is not a datetime' as DATETIME)",
				ExpectedWarning:                 1292,
				ExpectedWarningsCount:           1,
				ExpectedWarningMessageSubstring: "Incorrect datetime value: this is not a datetime",
				SkipResultsCheck:                true,
			},
			{
				Query:                           "SELECT CONVERT('this is not a date', DATE)",
				ExpectedWarning:                 1292,
				ExpectedWarningsCount:           1,
				ExpectedWarningMessageSubstring: "Incorrect date value: this is not a date",
				SkipResultsCheck:                true,
			},
			{
				Query:                           "SELECT CAST('this is not a date' as DATE)",
				ExpectedWarning:                 1292,
				ExpectedWarningsCount:           1,
				ExpectedWarningMessageSubstring: "Incorrect date value: this is not a date",
				SkipResultsCheck:                true,
			},
		},
	},
	{
		Name:    "Describe with expressions and views work correctly",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t(pk int primary key, val int DEFAULT (pk * 2))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"val", "int", "YES", "", "((`pk` * 2))", "DEFAULT_GENERATED"},
				},
			},
		},
	},
	{
		Name:    "Check support for deprecated BINARY attribute after character set",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (pk BIGINT PRIMARY KEY, v1 VARCHAR(255) COLLATE utf8mb4_0900_bin);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SHOW CREATE TABLE test;",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `pk` bigint NOT NULL,\n  `v1` varchar(255),\n  PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "ALTER TABLE test CHANGE v1 v2 VARCHAR(255) CHARACTER SET utf8mb4 BINARY NOT NULL;",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SHOW CREATE TABLE test;",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `pk` bigint NOT NULL,\n  `v2` varchar(255) COLLATE utf8mb4_bin NOT NULL,\n  PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "CREATE TABLE test2 (pk BIGINT PRIMARY KEY, v1 VARCHAR(255) CHARACTER SET utf8mb4 BINARY);",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SHOW CREATE TABLE test2;",
				Expected: []sql.Row{{"test2", "CREATE TABLE `test2` (\n  `pk` bigint NOT NULL,\n  `v1` varchar(255) COLLATE utf8mb4_bin,\n  PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
		},
	},
	{
		Name: "sum() and avg() on DECIMAL type column returns the DECIMAL type result",
		SetUpScript: []string{
			"create table decimal_table (id int, val decimal(18,16));",
			"insert into decimal_table values (1,-2.5633000000000384);",
			"insert into decimal_table values (2,2.5633000000000370);",
			"insert into decimal_table values (3,0.0000000000000004);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT val FROM decimal_table;",
				Expected: []sql.Row{{"-2.5633000000000384"}, {"2.5633000000000370"}, {"0.0000000000000004"}},
			},
			{
				Query:    "SELECT sum(val) FROM decimal_table;",
				Expected: []sql.Row{{"-0.0000000000000010"}},
			},
			{
				Query:    "SELECT avg(val) FROM decimal_table;",
				Expected: []sql.Row{{"-0.00000000000000033333"}},
			},
		},
	},
	{
		Name: "sum() and avg() on non-DECIMAL type column returns the DOUBLE type result",
		SetUpScript: []string{
			"create table float_table (id int primary key, val1 double, val2 float);",
			"insert into float_table values (1,-2.5633000000000384, 2.3);",
			"insert into float_table values (2,2.5633000000000370, 2.4);",
			"insert into float_table values (3,0.0000000000000004, 5.3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT sum(id), sum(val1), sum(val2) FROM float_table ORDER BY id;",
				Expected: []sql.Row{{float64(6), -9.322676295501879e-16, 10.000000238418579}},
			},
			{
				Query:    "SELECT sum(id), sum(val1), sum(val2) FROM float_table ORDER BY id;",
				Expected: []sql.Row{{float64(6), -9.322676295501879e-16, 10.000000238418579}},
			},
			{
				Query:    "SELECT avg(id), avg(val1), avg(val2) FROM float_table ORDER BY id;",
				Expected: []sql.Row{{float64(2), -3.107558765167293e-16, 3.333333412806193}},
			},
		},
	},
	{
		Name: "compare DECIMAL type columns with different precision and scale",
		SetUpScript: []string{
			"create table t (id int primary key, val1 decimal(2, 1), val2 decimal(3, 1));",
			"insert into t values (1, 1.2, 1.1), (2, 1.2, 10.1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select if(val1 < val2, 'YES', 'NO') from t order by id;",
				Expected: []sql.Row{{"NO"}, {"YES"}},
			},
		},
	},
	{
		Name:    "basic test on tables dual and `dual`",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE `dual` (id int)",
			"INSERT INTO `dual` VALUES (2)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT * from `dual`;",
				Expected: []sql.Row{{2}},
			},
			{
				Query:    "SELECT 3 from dual;",
				Expected: []sql.Row{{3}},
			},
			{
				Dialect:     "mysql",
				Query:       "SELECT * from dual;",
				ExpectedErr: sql.ErrNoTablesUsed,
			},
		},
	},
	{
		Name: "having clause without groupby clause, all rows implicitly form a single aggregate group",
		SetUpScript: []string{
			"create table numbers (val int);",
			"insert into numbers values (1), (2), (3);",
			"insert into numbers values (2), (4);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select val from numbers;",
				Expected: []sql.Row{{1}, {2}, {3}, {2}, {4}},
			},
			{
				Query:    "select val as a from numbers having a = val;",
				Expected: []sql.Row{{1}, {2}, {3}, {2}, {4}},
			},
			{
				Query:    "select val as a from numbers group by val having a = val;",
				Expected: []sql.Row{{1}, {2}, {3}, {4}},
			},
			{
				Query:    "select val as a from numbers as t1 group by t1.val having a = t1.val;",
				Expected: []sql.Row{{1}, {2}, {3}, {4}},
			},
			{
				Query:    "select t1.val as a from numbers as t1 group by 1 having a = t1.val;",
				Expected: []sql.Row{{1}, {2}, {3}, {4}},
			},
			{
				Query:    "select t1.val as a from numbers as t1 having a = t1.val;",
				Expected: []sql.Row{{1}, {2}, {3}, {2}, {4}},
			},
			{
				Query:    "select count(*) from numbers having count(*) = 5;",
				Expected: []sql.Row{{5}},
			},
			{
				// MySQL returns `Unknown column 'val' in 'having clause'` error for this query,
				// but GMS builds GroupBy for any aggregate function.
				Skip:  true,
				Query: "select count(*) from numbers having count(*) > val;",
				// ExpectedErrStr:   "found HAVING clause with no GROUP BY", // not the exact error we want
			},
			{
				Query:    "select count(*) from numbers group by val having count(*) < val;",
				Expected: []sql.Row{{1}, {1}},
			},
		},
	},
	{
		Name: "using having and group by clauses in subquery ",
		SetUpScript: []string{
			"CREATE TABLE t (i int, t varchar(2));",
			"insert into t values (1, 'a'), (1, 'a2'), (2, 'b'), (3, 'c'), (3, 'c2'), (4, 'd'), (5, 'e'), (5, 'e2');", // , (6, 'f'), (7, 'g'), (7, 'g2')
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select i from t group by i having count(1) = 1 order by i asc",
				Expected: []sql.Row{{2}, {4}},
			},
			{
				Query:    "select i from t group by i having count(1) != 1 order by i asc",
				Expected: []sql.Row{{1}, {3}, {5}},
			},
			{
				Query:    "select * from t where i in (select i from t group by i having count(1) = 1) order by i, t asc;",
				Expected: []sql.Row{{2, "b"}, {4, "d"}},
			},
			{
				Query:    "select * from t where i in (select i from t group by i having count(1) != 1) order by i, t asc;",
				Expected: []sql.Row{{1, "a"}, {1, "a2"}, {3, "c"}, {3, "c2"}, {5, "e"}, {5, "e2"}},
			},
			{
				Query:    "select * from t where i in (select i from t where i = 2 group by i having count(1) = 1) order by i, t asc;",
				Expected: []sql.Row{{2, "b"}},
			},
			{
				Query:    "select * from t where i in (select i from t where i = 3 group by i having count(1) != 1) order by i, t asc;",
				Expected: []sql.Row{{3, "c"}, {3, "c2"}},
			},
			{
				Query:    "select * from t where i in (select i from t where i > 2 group by i having count(1) != 1) order by i, t asc;",
				Expected: []sql.Row{{3, "c"}, {3, "c2"}, {5, "e"}, {5, "e2"}},
			},
			{
				Query:    "select * from t where i in (select i from t where i > 2 group by i having count(1) != 1 order by i desc) order by i, t asc;",
				Expected: []sql.Row{{3, "c"}, {3, "c2"}, {5, "e"}, {5, "e2"}},
			},
			{
				Query:    "select * from t where i in (select i from t where i > 2 group by i having count(1) != 1) order by i desc, t asc;",
				Expected: []sql.Row{{5, "e"}, {5, "e2"}, {3, "c"}, {3, "c2"}},
			},
		},
	},
	{
		Name: "can't create view with same name as existing table",
		SetUpScript: []string{
			"create table t (i int);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "create view t as select 1",
				ExpectedErr: sql.ErrTableAlreadyExists,
			},
		},
	},
	{
		Name: "can't create table with same name as existing view",
		SetUpScript: []string{
			"create view t as select 1",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "create table t (i int);",
				ExpectedErr: sql.ErrTableAlreadyExists,
			},
		},
	},
	{
		Name: "'/' division operation result in decimal or float",
		SetUpScript: []string{
			"create table floats (f float);",
			"insert into floats values (1.1), (1.2), (1.3);",
			"create table decimals (d decimal(2,1));",
			"insert into decimals values (1.0), (2.0), (2.5);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select f/2 from floats;",
				Expected: []sql.Row{{0.550000011920929}, {0.6000000238418579}, {0.6499999761581421}},
			},
			{
				Query:    "select 2/f from floats;",
				Expected: []sql.Row{{1.8181817787737895}, {1.6666666004392863}, {1.5384615948919735}},
			},
			{
				Query:    "select d/2 from decimals;",
				Expected: []sql.Row{{"0.50000"}, {"1.00000"}, {"1.25000"}},
			},
			{
				Query:    "select 2/d from decimals;",
				Expected: []sql.Row{{"2.0000"}, {"1.0000"}, {"0.8000"}},
			},
			{
				Query: "select f/d from floats, decimals;",
				Expected: []sql.Row{{1.2999999523162842}, {1.2000000476837158}, {1.100000023841858},
					{0.6499999761581421}, {0.6000000238418579}, {0.550000011920929},
					{0.5199999809265137}, {0.48000001907348633}, {0.4400000095367432}},
			},
			{
				Query: "select d/f from floats, decimals;",
				Expected: []sql.Row{{0.7692307974459868}, {0.8333333002196431}, {0.9090908893868948},
					{1.5384615948919735}, {1.6666666004392863}, {1.8181817787737895},
					{1.9230769936149668}, {2.083333250549108}, {2.272727223467237}},
			},
			{
				Dialect:  "mysql",
				Query:    `select f/'a' from floats;`,
				Expected: []sql.Row{{nil}, {nil}, {nil}},
			},
		},
	},
	{
		Name:    "'%' mod operation result in decimal or float",
		Dialect: "mysql", // % operator between types not defined in other dialects
		SetUpScript: []string{
			"create table a (pk int primary key, c1 int, c2 double, c3 decimal(5,3));",
			"insert into a values (1, 1, 1.111, 1.111), (2, 2, 2.111, 2.111);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select c1 % 2, c2 % 2, c3 % 2 from a;",
				Expected: []sql.Row{{"1", 1.111, "1.111"}, {"0", 0.11100000000000021, "0.111"}},
			},
			{
				Query:    "select c1 % 0.5, c2 % 0.5, c3 % 0.5 from a;",
				Expected: []sql.Row{{"0.0", 0.11099999999999999, "0.111"}, {"0.0", 0.11100000000000021, "0.111"}},
			},
			{
				Query:    "select 20 % c1, 20 % c2, 20 % c3 from a;",
				Expected: []sql.Row{{"0", 0.002000000000000224, "0.002"}, {"0", 1.0009999999999981, "1.001"}},
			},
		},
	},
	{
		Name: "arithmetic bit operations on int, float and decimal types",
		SetUpScript: []string{
			"CREATE TABLE num_types (pk int primary key, a int, b float, c decimal(5,3));",
			"insert into num_types values (1,1,1.1,1.1), (2,2,1.2,2.2), (3,3,1.6,3.7), (4,4,1.7,4.0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select a & 2.4, a | 2.4, a ^ 2.4 from num_types;",
				Expected: []sql.Row{
					{uint64(0), uint64(3), uint64(3)},
					{uint64(2), uint64(2), uint64(0)},
					{uint64(2), uint64(3), uint64(1)},
					{uint64(0), uint64(6), uint64(6)},
				},
			},
			{
				Query: "select b & 2.4, b | 2.4, b ^ 2.4 from num_types;",
				Expected: []sql.Row{
					{uint64(0), uint64(3), uint64(3)},
					{uint64(0), uint64(3), uint64(3)},
					{uint64(2), uint64(2), uint64(0)},
					{uint64(2), uint64(2), uint64(0)},
				},
			},
			{
				Query: "select c & 2.4, c | 2.4, c ^ 2.4 from num_types;",
				Expected: []sql.Row{
					{uint64(0), uint64(3), uint64(3)},
					{uint64(2), uint64(2), uint64(0)},
					{uint64(0), uint64(6), uint64(6)},
					{uint64(0), uint64(6), uint64(6)},
				},
			},
		},
	},
	{
		Name:    "year type behavior",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (pk int primary key, col1 year);",
		},
		Assertions: []ScriptTestAssertion{
			// 1901 - 2155 are interpreted as 1901 - 2155
			{
				Query:    "INSERT INTO t VALUES (1, '1901'), (2, 1901);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "INSERT INTO t VALUES (3, '2000'), (4, 2000);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "INSERT INTO t VALUES (5, '2155'), (6, 2155);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			// 1 - 69 are interpreted as 2001 - 2069
			{
				Query:    "INSERT INTO t VALUES (7, '1'), (8, 1);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "INSERT INTO t VALUES (9, '35'), (10, 35);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "INSERT INTO t VALUES (11, '69'), (12, 69);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			// 70 - 99 are interpreted as 1970 - 1999
			{
				Query:    "INSERT INTO t VALUES (13, '70'), (14, 70);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "INSERT INTO t VALUES (15, '85'), (16, 85);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			{
				Query:    "INSERT INTO t VALUES (17, '99'), (18, 99);",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			// '0', and '00' are interpreted as 2000
			{
				Query:    "INSERT INTO t VALUES (19, '0'), (20, '00');",
				Expected: []sql.Row{{types.NewOkResult(2)}},
			},
			// 0 is interpreted as 0000
			{
				Query:    "INSERT INTO t VALUES (21, 0)",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			// Assert that returned values are correct.
			{
				Query: "SELECT * from t order by pk;",
				Expected: []sql.Row{
					{1, int16(1901)},
					{2, int16(1901)},
					{3, int16(2000)},
					{4, int16(2000)},
					{5, int16(2155)},
					{6, int16(2155)},
					{7, int16(2001)},
					{8, int16(2001)},
					{9, int16(2035)},
					{10, int16(2035)},
					{11, int16(2069)},
					{12, int16(2069)},
					{13, int16(1970)},
					{14, int16(1970)},
					{15, int16(1985)},
					{16, int16(1985)},
					{17, int16(1999)},
					{18, int16(1999)},
					{19, int16(2000)},
					{20, int16(2000)},
					{21, int16(0)},
				},
			},
		},
	},
	{
		Name:    "INSERT IGNORE correctly truncates column data",
		Dialect: "mysql",
		SetUpScript: []string{
			`CREATE TABLE t (
				pk int primary key,
				col1 boolean,
				col2 integer,
				col3 tinyint,
				col4 smallint,
				col5 mediumint,
				col6 int,
				col7 bigint,
				col8 decimal,
				col9 float,
				col10 double,
				col11 date,
				col12 time,
				col13 datetime,
				col14 timestamp,
				col15 year,
				col16 ENUM('first', 'second'),
				col17 SET('a', 'b')
			);`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `
					INSERT IGNORE INTO t VALUES (
						1, 'val1', 'val2', 'val3', 'val4', 'val5', 'val6', 'val7', 'val8', 'val9', 'val10',
						'val11', 'val12', 'val13', 'val14', 'val15', 'val16', 'val17'
					);
				`,
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				SkipResultCheckOnServerEngine: true, // the datetime returned is not non-zero
				Query:                         "SELECT * from t",
				Expected: []sql.Row{
					{
						1,
						0,
						0,
						0,
						0,
						0,
						0,
						0,
						"0",
						float64(0),
						float64(0),
						time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC),
						types.Timespan(0),
						time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC),
						time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC),
						0,
						"",
						"",
					},
				},
			},
		},
	},
	{
		Name: "scientific notation for floats",
		SetUpScript: []string{
			"create table t (b bigint unsigned);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "insert into t values (5.2443381514267e+18);",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
		},
	},
	{
		Name: "INSERT IGNORE throws an error when json is badly formatted",
		SetUpScript: []string{
			"CREATE TABLE t (pk int primary key, col1 json);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "INSERT IGNORE into t VALUES (1, 'val1');",
				ExpectedErr: sql.ErrInvalidJson,
			},
		},
	},
	{
		Name:    "hash lookup for joins works with binary",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table uv (u int primary key, v int);",
			"create table xy (x int primary key, y int);",
			"insert into uv values (0,0), (1,1), (2,2);",
			"insert into xy values (0,0), (1,1), (2,2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select uv.u from uv join xy on binary xy.x = binary uv.u;",
				Expected: []sql.Row{
					{0},
					{1},
					{2},
				},
			},
		},
	},
	{
		Name: "identical expressions over different windows should produce different results",
		SetUpScript: []string{
			"CREATE TABLE t(a INT, b INT);",
			"INSERT INTO t(a, b) VALUES (1, 1), (1, 2), (1, 3), (2, 4), (2, 5), (2, 6);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT SUM(b) OVER (PARTITION BY a ORDER BY b) FROM t ORDER BY 1;",
				Expected: []sql.Row{{float64(1)}, {float64(3)}, {float64(4)}, {float64(6)}, {float64(9)}, {float64(15)}},
			},
			{
				Query:    "SELECT SUM(b) OVER (ORDER BY b) FROM t ORDER BY 1;",
				Expected: []sql.Row{{float64(1)}, {float64(3)}, {float64(6)}, {float64(10)}, {float64(15)}, {float64(21)}},
			},
			{
				Query: "SELECT SUM(b) OVER (PARTITION BY a ORDER BY b), SUM(b) OVER (ORDER BY b) FROM t ORDER BY 1;",
				Expected: []sql.Row{
					{float64(1), float64(1)},
					{float64(3), float64(3)},
					{float64(4), float64(10)},
					{float64(6), float64(6)},
					{float64(9), float64(15)},
					{float64(15), float64(21)},
				},
			},
		},
	},
	{
		Name: "windows without ORDER BY should be treated as RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING",
		SetUpScript: []string{
			"CREATE TABLE t(a INT, b INT);",
			"INSERT INTO t(a, b) VALUES (1, 1), (1, 2), (1, 3), (2, 4), (2, 5), (2, 6);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT SUM(b) OVER (PARTITION BY a) FROM t ORDER BY 1;",
				Expected: []sql.Row{{float64(6)}, {float64(6)}, {float64(6)}, {float64(15)}, {float64(15)}, {float64(15)}},
			},
			{
				Query:    "SELECT SUM(b) OVER () FROM t ORDER BY 1;",
				Expected: []sql.Row{{float64(21)}, {float64(21)}, {float64(21)}, {float64(21)}, {float64(21)}, {float64(21)}},
			},
			{
				Query: "SELECT SUM(b) OVER (PARTITION BY a), SUM(b) OVER () FROM t;",
				Expected: []sql.Row{
					{float64(6), float64(21)},
					{float64(6), float64(21)},
					{float64(6), float64(21)},
					{float64(15), float64(21)},
					{float64(15), float64(21)},
					{float64(15), float64(21)},
				},
			},
		},
	},
	{
		Name: "decimal literals should be parsed correctly",
		SetUpScript: []string{
			"SET @testValue = 809826404100301269648758758005707100;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT @testValue;",
				Expected: []sql.Row{{"809826404100301269648758758005707100"}},
			},
		},
	},
	{
		Name: "division and int division operation on negative, small and big value for decimal type column of table",
		SetUpScript: []string{
			"create table t (d decimal(25,10) primary key);",
			"insert into t values (-4990), (2), (22336578);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select d div 314990 from t order by d;",
				Expected: []sql.Row{{0}, {0}, {70}},
			},
			{
				Query:    "select d / 314990 from t order by d;",
				Expected: []sql.Row{{"-0.01584177275469"}, {"0.00000634940792"}, {"70.91202260389219"}},
			},
		},
	},
	{
		Name:    "drop table if exists on unknown table shows warning",
		Dialect: "mysql",
		Assertions: []ScriptTestAssertion{
			{
				Query:                           "DROP TABLE IF EXISTS non_existent_table;",
				ExpectedWarning:                 1051,
				ExpectedWarningsCount:           1,
				ExpectedWarningMessageSubstring: "Unknown table 'non_existent_table'",
				SkipResultsCheck:                true,
			},
		},
	},

	{
		Name:    "coalesce tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table c select coalesce(NULL, 1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from c;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select COLUMN_NAME, DATA_TYPE from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='c';",
				Expected: []sql.Row{
					{"coalesce(NULL, 1)", "int"},
				},
			},
		},
	},
	{
		Name: "Keyless Table with Unique Index",
		SetUpScript: []string{
			"create table a (x int, val int unique)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO a VALUES (1, 1)",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:       "INSERT INTO a VALUES (1, 1)",
				ExpectedErr: sql.ErrUniqueKeyViolation,
			},
		},
	},
	{
		Name:    "renaming views with RENAME TABLE ... TO .. statement",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t1 (id int primary key, v1 int);",
			"create view v1 as select * from t1;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "show tables;",
				Expected: []sql.Row{{"t1"}, {"v1"}},
			},
			{
				Query:    "rename table v1 to view1",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "show tables;",
				Expected: []sql.Row{{"t1"}, {"view1"}},
			},
			{
				Query:    "rename table view1 to newViewName, t1 to newTableName",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "show tables;",
				Expected: []sql.Row{{"newTableName"}, {"newViewName"}},
			},
		},
	},
	{
		Name:    "renaming views with ALTER TABLE ... RENAME .. statement should fail",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t1 (id int primary key, v1 int);",
			"create view v1 as select * from t1;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "show tables;",
				Expected: []sql.Row{{"t1"}, {"v1"}},
			},
			{
				Query:       "alter table v1 rename to view1",
				ExpectedErr: sql.ErrExpectedTableFoundView,
			},
			{
				Query:    "show tables;",
				Expected: []sql.Row{{"t1"}, {"v1"}},
			},
		},
	},
	{
		Name:    "timezone default settings",
		Dialect: "mysql",
		Assertions: []ScriptTestAssertion{
			{
				// TODO: Skipping this test while we figure out why this change causes the mysql java
				// connector integration test to fail.
				Skip: true,
				// To match MySQL's behavior, this comes from the operating system's timezone setting
				// TODO: the "global" shouldn't be necessary here, but GMS goes to session without it
				Query:    `select @@global.system_time_zone;`,
				Expected: []sql.Row{{sql.SystemTimezoneOffset()}},
			},
			{
				// The default time_zone setting for MySQL is SYSTEM, which means timezone comes from @@system_time_zone
				Query:    `select @@time_zone;`,
				Expected: []sql.Row{{"SYSTEM"}},
			},
		},
	},
	{
		Name:    "current time functions",
		Dialect: "mysql",
		Assertions: []ScriptTestAssertion{
			{
				// Smoke test that NOW() and UTC_TIMESTAMP() return non-null values with the SYSTEM time zone
				Query:    `select @@time_zone, NOW() IS NOT NULL, UTC_TIMESTAMP() IS NOT NULL;`,
				Expected: []sql.Row{{"SYSTEM", true, true}},
			},
			{
				// CURTIME() returns the same time as NOW() with the SYSTEM timezone
				// TODO: TIME(NOW()) would be simpler test logic, but doesn't work correctly here.
				Query:    `select @@time_zone, NOW() LIKE CONCAT('%', CURTIME(), '%');`,
				Expected: []sql.Row{{"SYSTEM", true}},
			},
			{
				// Set the timezone set to UTC as an offset
				Query:    `set @@time_zone='+00:00';`,
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				// When the session's time zone is set to UTC, NOW() and UTC_TIMESTAMP() should return the same value
				Query:    `select @@time_zone, NOW(6) = UTC_TIMESTAMP();`,
				Expected: []sql.Row{{"+00:00", true}},
			},
			{
				// CURTIME() returns the same time as NOW() with UTC's timezone offset
				Query:    `select @@time_zone, NOW() LIKE CONCAT('%', CURTIME(), '%');`,
				Expected: []sql.Row{{"+00:00", true}},
			},
			{
				Query:    `set @@time_zone='+02:00';`,
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				// When the session's time zone is set to +2:00, NOW() should report two hours ahead of UTC_TIMESTAMP()
				Query:    `select @@time_zone, TIMESTAMPDIFF(MINUTE, NOW(6), UTC_TIMESTAMP());`,
				Expected: []sql.Row{{"+02:00", -120}},
			},
			{
				// CURTIME() returns the same time as NOW() with a +2:00 timezone offset
				Query:    `select @@time_zone, NOW() LIKE CONCAT('%', CURTIME(), '%');`,
				Expected: []sql.Row{{"+02:00", true}},
			},
		},
	},
	{
		Name:    "timestamp timezone conversion",
		Dialect: "mysql",
		SetUpScript: []string{
			"set time_zone='+00:00';",
			"create table timezonetest(pk int primary key, dt datetime, ts timestamp);",
			"insert into timezonetest values(1, '2020-02-14 12:00:00', '2020-02-14 12:00:00');",
		},
		Assertions: []ScriptTestAssertion{
			{
				// When reading back the datetime and timestamp values in the same time zone we entered them,
				// we should get the exact same results back.
				Query: `select * from timezonetest;`,
				Expected: []sql.Row{{1,
					time.Date(2020, time.February, 14, 12, 0, 0, 0, time.UTC),
					time.Date(2020, time.February, 14, 12, 0, 0, 0, time.UTC)}},
			},
			{
				Query:    `set @@time_zone='-08:00';`,
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				// TODO: Unskip after adding support for converting timestamp values to/from session time_zone
				Skip: true,
				// After changing the session's time zone, we should get back a different result for the timestamp
				// column, but the same result for the datetime column.
				Query: `select * from timezonetest;`,
				Expected: []sql.Row{{1,
					time.Date(2020, time.February, 14, 12, 0, 0, 0, time.UTC),
					time.Date(2020, time.February, 14, 4, 0, 0, 0, time.UTC)}},
			},
			{
				Query:    `set @@time_zone='+5:00';`,
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				// Test with explicit timezone in datetime literal
				Query:    `insert into timezonetest values(3, '2020-02-16 12:00:00 +0800 CST', '2020-02-16 12:00:00 +0800 CST');`,
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				// TODO: Unskip after adding support for converting timestamp values to/from session time_zone
				Skip:  true,
				Query: `select * from timezonetest;`,
				Expected: []sql.Row{
					{1, time.Date(2020, time.February, 14, 12, 0, 0, 0, time.UTC),
						time.Date(2020, time.February, 14, 17, 0, 0, 0, time.UTC)},
					{3, time.Date(2020, time.February, 16, 9, 0, 0, 0, time.UTC),
						time.Date(2020, time.February, 16, 9, 0, 0, 0, time.UTC)}},
			},
			{
				Query:    `set @@time_zone='+0:00';`,
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				// TODO: Unskip after adding support for converting timestamp values to/from session time_zone
				Skip:  true,
				Query: `select * from timezonetest;`,
				Expected: []sql.Row{
					{1, time.Date(2020, time.February, 14, 12, 0, 0, 0, time.UTC),
						time.Date(2020, time.February, 14, 12, 0, 0, 0, time.UTC)},
					{3, time.Date(2020, time.February, 16, 9, 0, 0, 0, time.UTC),
						time.Date(2020, time.February, 16, 4, 0, 0, 0, time.UTC)}},
			},
		},
	},
	{
		Name: "test index scan over floats",
		SetUpScript: []string{
			"CREATE TABLE tab2(pk INTEGER PRIMARY KEY, col0 INTEGER, col1 FLOAT, col2 TEXT, col3 INTEGER, col4 FLOAT, col5 TEXT);",
			"CREATE UNIQUE INDEX idx_tab2_0 ON tab2 (col1 DESC,col4 DESC);",
			"CREATE INDEX idx_tab2_1 ON tab2 (col1,col0);",
			"CREATE INDEX idx_tab2_2 ON tab2 (col4,col0);",
			"CREATE INDEX idx_tab2_3 ON tab2 (col3 DESC);",
			"INSERT INTO tab2 VALUES(0,344,171.98,'nwowg',833,149.54,'wjiif');",
			"INSERT INTO tab2 VALUES(1,353,589.18,'femmh',44,621.85,'qedct');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT pk FROM tab2 WHERE ((((((col0 IN (SELECT col3 FROM tab2 WHERE ((col1 = 672.71)) AND col4 IN (SELECT col1 FROM tab2 WHERE ((col4 > 169.88 OR col0 > 939 AND ((col3 > 578))))) AND col0 >= 377) AND col4 >= 817.87 AND (col4 > 597.59)) OR col4 >= 434.59 AND ((col4 < 158.43)))))) AND col0 < 303) OR ((col0 > 549)) AND (col4 BETWEEN 816.92 AND 983.96) OR (col3 BETWEEN 421 AND 96);",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "empty table update",
		SetUpScript: []string{
			"create table t (i int primary key)",
			"insert into t values (1), (2), (3)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "update t set i = 0 where false",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0, InsertID: 0, Info: plan.UpdateInfo{Matched: 0}}}},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
		},
	},
	{
		Name:    "case insensitive index handling",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table table_One (Id int primary key, Val1 int);",
			"create table TableTwo (iD int primary key, VAL2 int, vAL3 int);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "create index idx_one on TABLE_ONE (vAL1);",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "show create table TABLE_one;",
				Expected: []sql.Row{{"table_One",
					"CREATE TABLE `table_One` (\n" +
						"  `Id` int NOT NULL,\n" +
						"  `Val1` int,\n" +
						"  PRIMARY KEY (`Id`),\n" +
						"  KEY `idx_one` (`Val1`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "show index from TABLE_one;",
				Expected: []sql.Row{
					{"table_One", 0, "PRIMARY", 1, "Id", nil, 0, nil, nil, "", "BTREE", "", "", "YES", nil},
					{"table_One", 1, "idx_one", 1, "Val1", nil, 0, nil, nil, "YES", "BTREE", "", "", "YES", nil},
				},
			},
			{
				Query:    "create index idx_one on TABLEtwo (VAL2, VAL3);",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "show create table TABLETWO;",
				Expected: []sql.Row{{"TableTwo", "CREATE TABLE `TableTwo` (\n" +
					"  `iD` int NOT NULL,\n" +
					"  `VAL2` int,\n" +
					"  `vAL3` int,\n" +
					"  PRIMARY KEY (`iD`),\n" +
					"  KEY `idx_one` (`VAL2`,`vAL3`)\n" +
					") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "show index from tABLEtwo;",
				Expected: []sql.Row{
					{"TableTwo", 0, "PRIMARY", 1, "iD", nil, 0, nil, nil, "", "BTREE", "", "", "YES", nil},
					{"TableTwo", 1, "idx_one", 1, "VAL2", nil, 0, nil, nil, "YES", "BTREE", "", "", "YES", nil},
					{"TableTwo", 1, "idx_one", 2, "vAL3", nil, 0, nil, nil, "YES", "BTREE", "", "", "YES", nil},
				},
			},
			{
				Query:    "drop index IDX_ONE on TABLE_one;",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "drop index IDX_ONE on TABLEtwo;",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "show create table TABLE_one;",
				Expected: []sql.Row{{"table_One",
					"CREATE TABLE `table_One` (\n" +
						"  `Id` int NOT NULL,\n" +
						"  `Val1` int,\n" +
						"  PRIMARY KEY (`Id`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "show create table TABLETWO;",
				Expected: []sql.Row{{"TableTwo", "CREATE TABLE `TableTwo` (\n" +
					"  `iD` int NOT NULL,\n" +
					"  `VAL2` int,\n" +
					"  `vAL3` int,\n" +
					"  PRIMARY KEY (`iD`)\n" +
					") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
		},
	},
	{
		Name: "different cases of function name should result in the same outcome",
		SetUpScript: []string{
			"create table t (b binary(2) primary key);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "select hex(*) from t;",
				ExpectedErr: sql.ErrStarUnsupported,
			},
			{
				Query:       "select HEX(*) from t;",
				ExpectedErr: sql.ErrStarUnsupported,
			},
			{
				Query:       "select HeX(*) from t;",
				ExpectedErr: sql.ErrStarUnsupported,
			},
		},
	},
	{
		Dialect: "mysql",
		Name:    "UNIX_TIMESTAMP function usage with session different time zones",
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SET time_zone = '+07:00';",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SELECT UNIX_TIMESTAMP('2023-09-25 07:02:57');",
				Expected: []sql.Row{{1695600177}},
			},
			{
				Query:    "SELECT UNIX_TIMESTAMP(CONVERT_TZ('2023-09-25 07:02:57', '+00:00', @@session.time_zone));",
				Expected: []sql.Row{{"1695625377.000000"}},
			},
			{
				Query:    "SET time_zone = '+00:00';",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SELECT UNIX_TIMESTAMP('2023-09-25 07:02:57');",
				Expected: []sql.Row{{1695625377}},
			},
			{
				Query:    "SET time_zone = '-06:00';",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SELECT UNIX_TIMESTAMP('2023-09-25 07:02:57');",
				Expected: []sql.Row{{1695646977}},
			},
		},
	},
	{
		Dialect: "mysql",
		Name:    "UNIX_TIMESTAMP function preserves trailing 0s",
		SetUpScript: []string{
			"SET time_zone = '+07:00';",
			"create table dt (dt0 datetime(0), dt1 datetime(1), dt2 datetime(2), dt3 datetime(3), dt4 datetime(4), dt5 datetime(5), dt6 datetime(6));",
			"insert into dt values ('2020-01-02 12:34:56.123456', '2020-01-02 12:34:56.123456', '2020-01-02 12:34:56.123456', '2020-01-02 12:34:56.123456', '2020-01-02 12:34:56.123456', '2020-01-02 12:34:56.123456', '2020-01-02 12:34:56.123456')",
			// TODO: time length not supported, so by default we have max precision
			"create table t (d date, tt time);",
			"insert into t values ('2020-01-02 12:34:56.123456', '12:34:56.123456');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select unix_timestamp('2001-02-03 12:34:56.10');",
				Expected: []sql.Row{
					{"981178496.10"},
				},
			},
			{
				Query: "select unix_timestamp('2001-02-03 12:34:56.000000');",
				Expected: []sql.Row{
					{"981178496.000000"},
				},
			},
			{
				Query: "select unix_timestamp('2001-02-03 12:34:56.1234567');",
				Expected: []sql.Row{
					{"981178496.123457"},
				},
			},
			{
				Query: "select unix_timestamp(dt0), unix_timestamp(dt1), unix_timestamp(dt2), unix_timestamp(dt3), unix_timestamp(dt4), unix_timestamp(dt5), unix_timestamp(dt6) from dt;",
				Expected: []sql.Row{
					{"1577943296", "1577943296.1", "1577943296.12", "1577943296.123", "1577943296.1235", "1577943296.12346", "1577943296.123456"},
				},
			},
			{
				Query: "select unix_timestamp(d), substring(cast(unix_timestamp(tt) as char(128)), -6) from t;",
				Expected: []sql.Row{
					{"1577898000", "123456"},
				},
			},
		},
	},

	{
		Name: "Querying existing view that references non-existing table",
		SetUpScript: []string{
			"CREATE TABLE a(id int primary key, col1 int);",
			"CREATE VIEW b AS SELECT * FROM a;",
			"CREATE VIEW f AS SELECT col1 AS npk FROM a;",
			"RENAME TABLE a TO d;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "CREATE VIEW g AS SELECT * FROM nonexistenttable;",
				ExpectedErr: sql.ErrTableNotFound,
			},
			{
				// TODO: ALTER VIEWs are not supported
				Skip:        true,
				Query:       "ALTER VIEW b AS SELECT * FROM nonexistenttable;",
				ExpectedErr: sql.ErrTableNotFound,
			},
			{
				Query:       "SELECT * FROM b;",
				ExpectedErr: sql.ErrInvalidRefInView,
			},
			{
				Query:    "RENAME TABLE d TO a;",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SELECT * FROM b;",
				Expected: []sql.Row{},
			},
			{
				Query:    "ALTER TABLE a RENAME COLUMN col1 TO newcol;",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				// TODO: View definition should have 'SELECT *' be expanded to each column of the referenced table
				Skip:        true,
				Query:       "SELECT * FROM b;",
				ExpectedErr: sql.ErrInvalidRefInView,
			},
			{
				Query:       "SELECT * FROM f;",
				ExpectedErr: sql.ErrInvalidRefInView,
			},
		},
	},
	{
		Name: "Multi-db Aliasing",
		SetUpScript: []string{
			"create database db1;",
			"create table db1.t1 (i int primary key);",
			"create table db1.t2 (j int primary key);",
			"insert into db1.t1 values (1);",
			"insert into db1.t2 values (2);",

			"create database db2;",
			"create table db2.t1 (i int primary key);",
			"create table db2.t2 (j int primary key);",
			"insert into db2.t1 values (10);",
			"insert into db2.t2 values (20);",
		},
		Assertions: []ScriptTestAssertion{
			{
				// surprisingly, this works
				Query: "select db1.t1.i from db1.t1 where db1.``.i > 0",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select db1.t1.i from db1.t1 where db1.t1.i > 0",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select db1.t1.i from db1.t1 order by db1.t1.i",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select db1.t1.i from db1.t1 group by db1.t1.i",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select db1.t1.i from db1.t1 having db1.t1.i > 0",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select (select db1.t1.i from db1.t1 order by db1.t1.i)",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select i from (select db1.t1.i from db1.t1 order by db1.t1.i) as t",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "with cte as (select db1.t1.i from db1.t1 order by db1.t1.i) select * from cte",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select i, j from db1.t1 inner join db2.t2 on 20 * i = j",
				Expected: []sql.Row{
					{1, 20},
				},
			},
			{
				Query: "select db1.t1.i, db2.t2.j from db1.t1 inner join db2.t2 on 20 * db1.t1.i = db2.t2.j",
				Expected: []sql.Row{
					{1, 20},
				},
			},
			{
				Query: "select i, j from db1.t1 join db2.t2 order by i, j",
				Expected: []sql.Row{
					{1, 20},
				},
			},
			{
				Query: "select i, j from db1.t1 join db2.t2 group by i order by j",
				Expected: []sql.Row{
					{1, 20},
				},
			},
			{
				Query: "select db1.t1.i, db2.t2.j from db1.t1 join db2.t2 group by db1.t1.i order by db2.t2.j",
				Expected: []sql.Row{
					{1, 20},
				},
			},
			{
				Skip:  true, // incorrectly throws Not unique table/alias: t1
				Query: "select db1.t1.i, db2.t1.i from db1.t1 join db2.t1 order by db1.t1, db2.t1.i",
				Expected: []sql.Row{
					{1, 10},
				},
			},
			{
				// Aliasing solves it
				Query: "select a.i, b.i from db1.t1 a join db2.t1 b order by a.i, b.i",
				Expected: []sql.Row{
					{1, 10},
				},
			},
		},
	},
	{
		Name: "order by with index",
		SetUpScript: []string{
			"create table t (i int primary key, `100` int);",
			"insert into t values (1, 2), (2, 1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t order by `100`",
				Expected: []sql.Row{
					{2, 1},
					{1, 2},
				},
			},
			{
				Query:          "select * from t order by 100",
				ExpectedErrStr: "column \"100\" could not be found in any table in scope",
			},
			{
				Query: "select i as `200`, `100` from t order by `200`",
				Expected: []sql.Row{
					{1, 2},
					{2, 1},
				},
			},
			{
				Query:          "select i as `200` from t order by 200",
				ExpectedErrStr: "column \"200\" could not be found in any table in scope",
			},
			{
				Query:          "select * from t order by 0",
				ExpectedErrStr: "column \"0\" could not be found in any table in scope",
			},
			{
				Query: "select * from t order by -999",
				Expected: []sql.Row{
					{1, 2},
					{2, 1},
				},
			},
		},
	},
	{
		Name: "Point lookups with dropped filters",
		SetUpScript: []string{
			`create table t1 (
    			  id varchar(255),
    			  a  varchar(255),
    			  unique key key1 (id, a)
    			);`,
			`create table t2 (
    			  id varchar(255),
    			  b  varchar(255),
    			  unique key key2 (id, b)
    			);`,
			`insert into t1 values 
    			  ('id1', 'a1'),
    			  ('id1', 'a2');`,
			`insert into t2 values
    			  ('id1', 'b1'),
    			  ('id1', 'b2'),
    			  ('id1', 'b3'),
    			  ('id2', 'b4'),
    			  ('id2', 'b5');`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `
    				select /*+ LOOKUP_JOIN(t1, t3)*/ t1.id, t1.a, t2.b from
                      t1
                    inner join
                      t2
                    on
                      t1.id = t2.id and t1.a = t2.b;`,
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "Complex Filter Index Scan",
		SetUpScript: []string{
			`CREATE TABLE tab2 (
              pk int NOT NULL,
              col0 int,
              col1 float,
              col2 text,
              col3 int,
              col4 float,
              col5 text,
              PRIMARY KEY (pk),
              UNIQUE KEY idx_tab2_0 (col3,col4),
              UNIQUE KEY idx_tab2_1 (col1,col4),
              UNIQUE KEY idx_tab2_2 (col3,col0,col4),
              UNIQUE KEY idx_tab2_3 (col1,col3)
            );`,
			`insert into tab2 values ( 63, 587, 465.59 , 'aggxb', 303 , 763.91, 'tgpqr');`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT pk FROM tab2 WHERE col4 IS NULL OR col0 > 560 AND (col3 < 848) OR (col3 > 883) OR (((col4 >= 539.78 AND col3 <= 953))) OR ((col3 IN (258)) OR (col3 IN (583,234,372)) AND col4 >= 488.43)",
				Expected: []sql.Row{
					{63},
				},
			},
		},
	},
	{
		Name: "Complex Filter Index Scan #2",
		SetUpScript: []string{
			"create table t (pk int primary key, v1 int, v2 int, v3 int, v4 int);",
			"create index v_idx on t (v1, v2, v3, v4);",
			"insert into t values (0, 26, 24, 91, 0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t where (((v1>25 and v2 between 23 and 54) or (v1<>40 and v3>90)) or (v1<>7 and v4<=78));",
				Expected: []sql.Row{
					{0, 26, 24, 91, 0},
				},
			},
		},
	},
	{
		Name: "Complex Filter Index Scan #3",
		SetUpScript: []string{
			"create table t (pk integer primary key, col0 integer, col1 float);",
			"create index idx on t (col0, col1);",
			"insert into t values (0, 22, 1.23);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select pk, col0 from t where (col0 in (73,69)) or col0 in (4,12,3,17,70,20) or (col0 in (39) or (col1 < 69.67));",
				Expected: []sql.Row{
					{0, 22},
				},
			},
		},
	},
	{
		Name: "update columns with default",
		SetUpScript: []string{
			"create table t (i int default 10, j varchar(128) default (concat('abc', 'def')));",
			"insert into t values (100, 'a'), (200, 'b');",
			"create table t2 (i int);",
			"insert into t2 values (1), (2), (3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "update t set i = default where i = 100;",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1, Info: plan.UpdateInfo{Matched: 1, Updated: 1}}},
				},
			},
			{
				Query: "select * from t order by i",
				Expected: []sql.Row{
					{10, "a"},
					{200, "b"},
				},
			},
			{
				Query: "update t set j = default where i = 200;",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1, Info: plan.UpdateInfo{Matched: 1, Updated: 1}}},
				},
			},
			{
				Query: "select * from t order by i",
				Expected: []sql.Row{
					{10, "a"},
					{200, "abcdef"},
				},
			},
			{
				Query: "update t set i = default, j = default;",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 2, Info: plan.UpdateInfo{Matched: 2, Updated: 2}}},
				},
			},
			{
				Query: "select * from t order by i",
				Expected: []sql.Row{
					{10, "abcdef"},
					{10, "abcdef"},
				},
			},
			{
				Query: "update t2 set i = default",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 3, Info: plan.UpdateInfo{Matched: 3, Updated: 3}}},
				},
			},
			{
				Query: "select * from t2",
				Expected: []sql.Row{
					{nil},
					{nil},
					{nil},
				},
			},
		},
	},
	{
		Name: "int index with float filter",
		SetUpScript: []string{
			"create table t0 (i int primary key);",
			"insert into t0 values (-1), (0), (1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t0 where i > 0.0 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i > 0.1 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i > 0.5 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i > 0.9 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query:    "select * from t0 where i > 1.0 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t0 where i > 1.1 order by i;",
				Expected: []sql.Row{},
			},

			{
				Query: "select * from t0 where i > -0.0 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i > -0.1 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i > -0.5 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i > -0.9 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i > -1.0 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i > -1.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},

			{
				Query: "select * from t0 where i >= 0.0 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= 0.1 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= 0.5 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= 0.9 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= 1.0 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query:    "select * from t0 where i >= 1.1 order by i;",
				Expected: []sql.Row{},
			},

			{
				Query: "select * from t0 where i >= -0.0 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= -0.1 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= -0.5 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= -0.9 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= -1.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i >= -1.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},

			{
				Query: "select * from t0 where i < 0.0 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i < 0.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i < 0.5 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i < 0.9 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i < 1.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i < 1.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},

			{
				Query: "select * from t0 where i < -0.0 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i < -0.1 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i < -0.5 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i < -0.9 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query:    "select * from t0 where i < -1.0 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t0 where i < -1.1 order by i;",
				Expected: []sql.Row{},
			},

			{
				Query: "select * from t0 where i <= 0.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i <= 0.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i <= 0.5 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i <= 0.9 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i <= 1.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i <= 1.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},

			{
				Query: "select * from t0 where i <= -0.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				Query: "select * from t0 where i <= -0.1 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i <= -0.5 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i <= -0.9 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query: "select * from t0 where i <= -1.0 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},
			{
				Query:    "select * from t0 where i <= -1.1 order by i;",
				Expected: []sql.Row{},
			},

			{
				Query: "select * from t0 where i = 0.0 order by i;",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query:    "select * from t0 where i = 0.1 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t0 where i = 0.5 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t0 where i = 0.9 order by i;",
				Expected: []sql.Row{},
			},

			{
				Query: "select * from t0 where i = -0.0 order by i;",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query:    "select * from t0 where i = -0.1 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t0 where i = -0.5 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t0 where i = -0.9 order by i;",
				Expected: []sql.Row{},
			},
			{
				Query: "select * from t0 where i = -1.0 order by i;",
				Expected: []sql.Row{
					{-1},
				},
			},

			{
				Query: "select * from t0 where i != 0.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != 0.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != 0.5 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != 0.9 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},

			{
				Query: "select * from t0 where i != -0.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != -0.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != -0.5 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != -0.9 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i != -1.0 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},

			{
				Query: "select * from t0 where i <= 0.0 and i >= 0.0 order by i;",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: "select * from t0 where i <= 0.1 or i >= 0.1 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i > 0.1 and i >= 0.1 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from t0 where i > 0.1 or i >= 0.1 order by i;",
				Expected: []sql.Row{
					{1},
				},
			},
		},
	},
	{
		Name: "int secondary index with float filter",
		SetUpScript: []string{
			"create table t0 (i int);",
			"create index idx on t0(i);",
			"insert into t0 values (null), (-1), (0), (1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t0 where i >= 0.0 order by i;",
				Expected: []sql.Row{
					{0},
					{1},
				},
			},
			{
				Query: "select * from t0 where i <= 0.0 order by i;",
				Expected: []sql.Row{
					{-1},
					{0},
				},
			},
			{
				// cot(-939932070) = -1.1919623754564008
				Query: "SELECT * from t0 where (cot(-939932070) < i);",
				Expected: []sql.Row{
					{-1},
					{0},
					{1},
				},
			},
		},
	},
	{
		Name: "decimal and float in tuple",
		SetUpScript: []string{
			"create table t (d decimal(10, 3), f float);",
			"insert into t values (0.8, 0.8);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from t where (d in (null, 1));",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t where (f in (null, 1));",
				Expected: []sql.Row{},
			},
			{
				// select count to avoid floating point comparison
				Query: "select count(*) from t where (d in (null, 0.8));",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				// This actually matches MySQL behavior
				Query:    "select * from t where (f in (null, 0.8));",
				Expected: []sql.Row{},
			},
			{
				// This actually matches MySQL behavior
				Query: "select count(*) from t where (f in (null, 0.8));",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				// select count to avoid floating point comparison
				Query: "select count(*) from t where (f in (null, cast(0.8 as float)));",
				Expected: []sql.Row{
					{1},
				},
			},
		},
	},
	{
		Name:    "floats in tuple are properly hashed",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (b bool);",
			"insert into t values (false);",
			"create table t_idx (b bool);",
			"create index idx on t_idx(b);",
			"insert into t_idx values (false);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t where (b in (-''));",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: "select * from t where (b in (false/'1'));",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: "select * from t_idx where (b in (-''));",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: "select * from t_idx where (b in (false/'1'));",
				Expected: []sql.Row{
					{0},
				},
			},
		},
	},
	{
		Name:    "hash in tuple picks correct type and skips mixed types",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (v varchar(10));",
			"insert into t values ('abc'), ('def'), ('ghi');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from t where (v in ('xyz')) order by v;",
				Expected: []sql.Row{},
			},
			{
				Query: "select * from t where (v in (0, 'xyz')) order by v;",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
					{"ghi"},
				},
			},
			{
				Query:    "select * from t where (v in (1, 'xyz')) order by v;",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name:    "strings in tuple are properly hashed",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (v varchar(100));",
			"insert into t values (false);",
			"create table t_idx (v varchar(100));",
			"create index idx on t_idx(v);",
			"insert into t_idx values (false);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t where (v in (-''));",
				Expected: []sql.Row{
					{"0"},
				},
			},
			{
				Query: "select * from t where (v in (false/'1'));",
				Expected: []sql.Row{
					{"0"},
				},
			},
			{
				Query: "select * from t_idx where (v in (-''));",
				Expected: []sql.Row{
					{"0"},
				},
			},
			{
				Query: "select * from t_idx where (v in (false/'1'));",
				Expected: []sql.Row{
					{"0"},
				},
			},
		},
	},
	{
		Name: "strings vs decimals with trailing 0s in IN exprs",
		SetUpScript: []string{
			"create table t (v varchar(100));",
			"insert into t values ('0'), ('0.0'), ('123'), ('123.0');",
			"create table t_idx (v varchar(100));",
			"create index idx on t_idx(v);",
			"insert into t_idx values ('0'), ('0.0'), ('123'), ('123.0');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Skip:  true,
				Query: "select * from t where (v in (0.0, 123));",
				Expected: []sql.Row{
					{"0"},
					{"0.0"},
					{"123"},
					{"123.0"},
				},
			},
			{
				Skip:  true,
				Query: "select * from t_idx where (v in (0.0, 123));",
				Expected: []sql.Row{
					{"0"},
					{"0.0"},
					{"123"},
					{"123.0"},
				},
			},
		},
	},
	{
		Name: "subquery with range heap join",
		SetUpScript: []string{
			"create table a (i int primary key, start int, end int, name varchar(32));",
			"insert into a values (1, 603000, 605001, 'test');",
			"create table b (i int primary key);",
			"insert into b values (600000), (605000), (608000);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select a.i from (select 'test' as name) sq join a on sq.name = a.name join b on b.i between a.start and a.end;",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select * from (select 'test' as name, 1 as x, 2 as y, 3 as z) sq join a on sq.name = a.name join b on b.i between a.start and a.end;",
				Expected: []sql.Row{
					{"test", 1, 2, 3, 1, 603000, 605001, "test", 605000},
				},
			},
		},
	},
	{
		Name:    "resolve foreign key on indexed update",
		Dialect: "mysql", // no way to disable foreign keys in doltgres yet
		SetUpScript: []string{
			"set foreign_key_checks=0;",
			"create table parent (i int primary key);",
			"create table child (i int primary key, foreign key (i) references parent(i));",
			"set foreign_key_checks=1;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "update child set i = 1 where i = 1;",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 0, Info: plan.UpdateInfo{Matched: 0, Updated: 0}}},
				},
			},
		},
	},
	{
		Name:    "between type conversion",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t0(c0 bool);",
			"create table t1(c1 bool);",
			"insert into t0 (c0) values (1);",
			"insert into t1 (c1) values (false), (true);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT t0.c0, t1.c1 FROM t0 LEFT  JOIN t1 ON true;",
				Expected: []sql.Row{
					{1, 0},
					{1, 1},
				},
			},
			{
				Query: "SELECT t0.c0, t1.c1 FROM t0 LEFT  JOIN t1 ON ('a' NOT BETWEEN false AND false) WHERE 1 UNION ALL SELECT t0.c0, t1.c1 FROM t0 LEFT  JOIN t1 ON ('a' NOT BETWEEN false AND false) WHERE (NOT 1) UNION ALL SELECT t0.c0, t1.c1 FROM t0 LEFT  JOIN t1 ON ('a' NOT BETWEEN false AND false) WHERE (1 IS NULL);",
				Expected: []sql.Row{
					{1, nil},
				},
			},
		},
	},
	{
		Name: "case sensitive subquery column names",
		SetUpScript: []string{
			"create table t(ABC int, dEF int);",
			"insert into t values (1, 2);",
		},

		Assertions: []ScriptTestAssertion{
			{
				ExpectedColumns: sql.Schema{
					{Name: "ABC", Type: types.Int32},
					{Name: "dEF", Type: types.Int32},
				},
				Query: "select * from t ",
				Expected: []sql.Row{
					{1, 2},
				},
			},
			{
				ExpectedColumns: sql.Schema{
					{Name: "ABC", Type: types.Int32},
					{Name: "dEF", Type: types.Int32},
				},
				Query: "select * from (select * from t) sqa",
				Expected: []sql.Row{
					{1, 2},
				},
			},
		},
	},
	{
		Name:    "bool and string",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t0(c0 BOOL, PRIMARY KEY(c0));",
			"INSERT INTO t0 (c0) VALUES (true);",
			"CREATE TABLE t1(c1 VARCHAR(500));",
			"INSERT INTO t1 (c1) VALUES (true);",
		},

		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT * FROM t1, t0;",
				Expected: []sql.Row{
					{"1", 1},
				},
			},
			{
				Query: "SELECT (t1.c1 = t0.c0) FROM t1, t0;",
				Expected: []sql.Row{
					{true},
				},
			},
			{
				Query: "SELECT * FROM t1, t0 WHERE t1.c1 = t0.c0;",
				Expected: []sql.Row{
					{"1", 1},
				},
			},
		},
	},
	{
		Name:    "bool and int",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t0(c0 INTEGER, PRIMARY KEY(c0));",
			"INSERT INTO t0 (c0) VALUES (true);",
			"CREATE TABLE t1(c1 VARCHAR(500));",
			"INSERT INTO t1 (c1) VALUES (true);",
		},

		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT * FROM t1, t0;",
				Expected: []sql.Row{
					{"1", 1},
				},
			},
			{
				Query: "SELECT (t1.c1 = t0.c0) FROM t1, t0;",
				Expected: []sql.Row{
					{true},
				},
			},
			{
				Query: "SELECT * FROM t1, t0 WHERE t1.c1 = t0.c0;",
				Expected: []sql.Row{
					{"1", 1},
				},
			},
		},
	},
	{
		Name: "update with left join with some missing rows",
		SetUpScript: []string{
			`create table joinparent (
				id int not null auto_increment,
				name varchar(128) not null,
				archived int default 0 not null,
				archived_at datetime null,
				primary key (id)
			);`,
			`insert into joinparent (name) values
				('first'),
				('second'),
				('third'),
				('fourth'),
				('fifth');`,
			`create index joinparent_archived on joinparent (archived, archived_at);`,
			`create table joinchild (
				id int not null auto_increment,
				name varchar(128) not null,
				parent_id int not null,
				archived int default 0 not null,
				archived_at datetime null,
				primary key (id),
				constraint joinchild_parent unique (parent_id, id, archived));`,
			`insert into joinchild (name, parent_id) values
				('first', 4),
				('second', 3),
				('third', 2);`,
		},
		Assertions: []ScriptTestAssertion{
			{
				// TODO: this query isn't valid SQL, why
				Query: `update joinparent as jp 
							left join joinchild as jc on jc.parent_id = jp.id
								set jp.archived = jp.id, jp.archived_at = now(), 
									jc.archived = jc.id, jc.archived_at = now()
						where jp.id > 0 and jp.name != "never"
						limit 100`,
				Expected: []sql.Row{{types.OkResult{RowsAffected: 8, Info: plan.UpdateInfo{Matched: 8, Updated: 8}}}},
			},
			// do without limit to use `plan.Sort` instead of `plan.TopN`
			{
				Query: `update joinparent as jp 
							left join joinchild as jc on jc.parent_id = jp.id
								set jp.archived = 0, jp.archived_at = null, 
									jc.archived = 0, jc.archived_at = null
						where jp.id > 0 and jp.name != "never"`,
				Expected: []sql.Row{{types.OkResult{RowsAffected: 8, Info: plan.UpdateInfo{Matched: 8, Updated: 8}}}},
			},
		},
	},
	{
		Name: "count distinct decimals",
		SetUpScript: []string{
			"create table t (i int, j int)",
			"insert into t values (1, 11), (11, 1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select count(distinct i, j) from t;",
				Expected: []sql.Row{
					{2},
				},
			},
			{
				Query: "select count(distinct cast(i as decimal), cast(j as decimal)) from t;",
				Expected: []sql.Row{
					{2},
				},
			},
		},
	},
	{
		Name:    "range query convert int to string zero value",
		Dialect: "mysql",
		SetUpScript: []string{
			`CREATE TABLE t0(c0 VARCHAR(500));`,
			`INSERT INTO t0(c0) VALUES ('a');`,
			`INSERT INTO t0(c0) VALUES ('1');`,
			`CREATE TABLE t1(c0 INTEGER, PRIMARY KEY(c0));`,
			`INSERT INTO t1(c0) VALUES (0);`,
			`INSERT INTO t1(c0) VALUES (1);`,
			`INSERT INTO t1(c0) VALUES (2);`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT /*+ LOOKUP_JOIN(t0,t1) JOIN_ORDER(t0,t1) */ * FROM t1 INNER  JOIN t0 ON ((t0.c0)=(t1.c0));",
				Expected: []sql.Row{
					{0, "a"},
					{1, "1"},
				},
			},
			{
				Query: "INSERT INTO t0(c0) VALUES ('2abc');",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Skip:  true,
				Query: "SELECT /*+ LOOKUP_JOIN(t0,t1) JOIN_ORDER(t0,t1) */ * FROM t1 INNER  JOIN t0 ON ((t0.c0)=(t1.c0));",
				Expected: []sql.Row{
					{0, "a"},
					{1, "1"},
					{2, "2abc"},
				},
			},
		},
	},
	{
		Name: "group by having with conflicting aliases test",
		SetUpScript: []string{
			"CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER);",
			"INSERT INTO tab2 VALUES(15,61,87);",
			"INSERT INTO tab2 VALUES(91,59,79);",
			"INSERT INTO tab2 VALUES(92,41,58);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `SELECT - col2 AS col0 FROM tab2 GROUP BY col0, col2 HAVING NOT + + col2 <= - col0;`,
				Expected: []sql.Row{
					{-87},
					{-79},
					{-58},
				},
			},
			{
				Query: `SELECT -col2 AS col0 FROM tab2 GROUP BY col0, col2 HAVING NOT col2 <= - col0;`,
				Expected: []sql.Row{
					{-87},
					{-79},
					{-58},
				},
			},
			{
				Query: `SELECT -col2 AS col0 FROM tab2 GROUP BY col0, col2 HAVING col2 > -col0;`,
				Expected: []sql.Row{
					{-87},
					{-79},
					{-58},
				},
			},
			{
				Query: `SELECT 500 * col2 AS col0 FROM tab2 GROUP BY col0, col2 HAVING col2 > -col0;`,
				Expected: []sql.Row{
					{43500},
					{39500},
					{29000},
				},
			},

			{
				Query: `select col2-100 as col0 from tab2 group by col0 having col0 > 0;`,
				Expected: []sql.Row{
					{-13},
					{-21},
					{-42},
				},
			},
			{
				Query:    `select col2-100 as col0 from tab2 group by 1 having col0 > 0;`,
				Expected: []sql.Row{},
			},
			{
				Query: `select col0, count(col0) as c from tab2 group by col0 having c > 0;`,
				Expected: []sql.Row{
					{15, 1},
					{91, 1},
					{92, 1},
				},
			},
			{
				Query: `SELECT col0 as a FROM tab2 GROUP BY a HAVING col0 = a;`,
				Expected: []sql.Row{
					{15},
					{91},
					{92},
				},
			},
			{
				Query: `SELECT col0 as a FROM tab2 GROUP BY col0 HAVING col0 = a;`,
				Expected: []sql.Row{
					{15},
					{91},
					{92},
				},
			},
			{
				Query: `SELECT col0 as a FROM tab2 GROUP BY col0, a HAVING col0 = a;`,
				Expected: []sql.Row{
					{15},
					{91},
					{92},
				},
			},
			{
				Query: `SELECT col0 as a FROM tab2 HAVING col0 = a;`,
				Expected: []sql.Row{
					{15},
					{91},
					{92},
				},
			},
			{
				Query: `select col0, (select col1 having col0 > 0) as asdf from tab2 where col0 < 1000;`,
				Expected: []sql.Row{
					{15, 61},
					{91, 59},
					{92, 41},
				},
			},
			{
				Query: `select col0, sum(col1 * col2) as val from tab2 group by col0 having sum(col1 * col2) > 0;`,
				Expected: []sql.Row{
					{15, 5307.0},
					{91, 4661.0},
					{92, 2378.0},
				},
			},
			{
				Query:       `SELECT col0+1 as a FROM tab2 HAVING col0 = a;`,
				ExpectedErr: sql.ErrColumnNotFound,
			},
			{
				Query:       `select col2-100 as asdf from tab2 group by 1 having col0 > 0;`,
				ExpectedErr: sql.ErrColumnNotFound,
			},
			{
				Query:       `SELECT -col2 AS col0 FROM tab2 HAVING col2 > -col0;`,
				ExpectedErr: sql.ErrColumnNotFound,
			},
			{
				Query:       `insert into tab2(col2) select sin(col2) from tab2 group by 1 having col2 > 1;`,
				ExpectedErr: sql.ErrColumnNotFound,
			},
		},
	},
	{
		Name: "dividing has different rounding behavior",
		SetUpScript: []string{
			"CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER);",
			"INSERT INTO tab0 VALUES(97, 1, 99);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT col2 IN ( 98 + col0 / 99 ) from tab0;",
				Expected: []sql.Row{
					{false},
				},
			},
			{
				Query: "SELECT col2 IN ( 98 + 97 / 99 ) from tab0;",
				Expected: []sql.Row{
					{false},
				},
			},
			{
				Query:    "SELECT * FROM tab0 WHERE col2 IN ( 98 + 97 / 99 );",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT ALL * FROM tab0 AS cor0 WHERE col2 IN ( 39 + + 89, col0 + + col1 + + ( - ( - col0 ) ) / col2, + ( col0 ) + - 99, + col1, + col2 * - + col2 * - 12 + col1 + - 66 );",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "complicated range tree",
		SetUpScript: []string{
			"create table t1 (a1 int, b1 int, primary key(a1, b1));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `
SELECT *
FROM t1
WHERE
    a1 in (702, 584, 607, 479, 330, 445, 513, 678, 406, 314, 880, 953, 75, 268) OR
    b1 in (213, 55,  992, 922, 619, 972, 654, 130,  88, 141, 679, 761) OR
    (a1=145 AND b1=818);
`,
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "many joins with chain of ANDs",
		SetUpScript: []string{
			"create table t1  (a1  int primary key, b1  int);",
			"create table t2  (a2  int primary key, b2  int);",
			"create table t3  (a3  int primary key, b3  int);",
			"create table t4  (a4  int primary key, b4  int);",
			"create table t5  (a5  int primary key, b5  int);",
			"create table t6  (a6  int primary key, b6  int);",
			"create table t7  (a7  int primary key, b7  int);",
			"create table t8  (a8  int primary key, b8  int);",
			"create table t9  (a9  int primary key, b9  int);",
			"create table t10 (a10 int primary key, b10 int);",
			"insert into t1 values  (1, 1);",
			"insert into t2 values  (1, 1);",
			"insert into t3 values  (1, 1);",
			"insert into t4 values  (1, 1);",
			"insert into t5 values  (1, 1);",
			"insert into t6 values  (1, 1);",
			"insert into t7 values  (1, 1);",
			"insert into t8 values  (1, 1);",
			"insert into t9 values  (1, 1);",
			"insert into t10 values (1, 1);",
			"insert into t1 values  (2, 2);",
			"insert into t2 values  (2, 2);",
			"insert into t3 values  (2, 2);",
			"insert into t4 values  (2, 2);",
			"insert into t5 values  (2, 2);",
			"insert into t6 values  (2, 2);",
			"insert into t7 values  (2, 2);",
			"insert into t8 values  (2, 2);",
			"insert into t9 values  (2, 2);",
			"insert into t10 values (2, 2);",
			"insert into t1 values  (3, 3);",
			"insert into t2 values  (3, 3);",
			"insert into t3 values  (3, 3);",
			"insert into t4 values  (3, 3);",
			"insert into t5 values  (3, 3);",
			"insert into t6 values  (3, 3);",
			"insert into t7 values  (3, 3);",
			"insert into t8 values  (3, 3);",
			"insert into t9 values  (3, 3);",
			"insert into t10 values (3, 3);",
			"insert into t1 values  (4, 4);",
			"insert into t2 values  (4, 4);",
			"insert into t3 values  (4, 4);",
			"insert into t4 values  (4, 4);",
			"insert into t5 values  (4, 4);",
			"insert into t6 values  (4, 4);",
			"insert into t7 values  (4, 4);",
			"insert into t8 values  (4, 4);",
			"insert into t9 values  (4, 4);",
			"insert into t10 values (4, 4);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `
select 
    a1, a2, a3, a4, a5, a6, a7, a8, a9, a10
from
    t1, t2, t3, t4, t5, t6, t7, t8, t9, t10
where
      1 = a3  and
     b9 = a3  and
     b2 = a9  and
    b10 = a2  and
     b5 = a10 and
     b7 = a5  and
     b4 = a7  and
     b1 = a4  and
     b8 = a1  and
     b6 = a8
;
`,
				Expected: []sql.Row{
					{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
				},
			},
		},
	},
	{
		Name: "preserve now()",
		SetUpScript: []string{
			"create table t1 (i int default (cast(now() as signed)));",
			"create table t2 (i int default (cast(current_timestamp(6) as signed)));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t1",
				Expected: []sql.Row{
					{"t1", "CREATE TABLE `t1` (\n" +
						"  `i` int DEFAULT (convert(NOW(), signed))\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "show create table t2",
				Expected: []sql.Row{
					{"t2", "CREATE TABLE `t2` (\n" +
						"  `i` int DEFAULT (convert(NOW(6), signed))\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},
	{
		Name: "binary type primary key",
		SetUpScript: []string{
			"create table t (b binary(3) primary key);",
			"insert into t values ('abc'), ('def'), ('ghi');",
			"create table tt (b binary(10) primary key);",
			"insert into tt values ('abc'), ('def'), ('ghi');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select cast(b as char) from t where b < cast('def' as binary(3));",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select cast(b as char) from t where b = cast('def' as binary(3));",
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Query: "select cast(b as char) from t where b > cast('def' as binary(3));",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: "select cast(b as char(3)) from tt where b < cast('def' as binary(10));",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select cast(b as char(3)) from tt where b = cast('def' as binary(10));",
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Query: "select cast(b as char(3)) from tt where b > cast('def' as binary(10));",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
		},
	},
	{
		Name: "varchar primary key",
		SetUpScript: []string{
			"create table vt (v varchar(3) primary key);",
			"insert into vt values ('abc'), ('def'), ('ghi');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from vt where v = 'def';",
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Query: "select * from vt where v < 'def';",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select * from vt where v > 'def';",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: "select * from vt where v <= 'def';",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select * from vt where v >= 'def';",
				Expected: []sql.Row{
					{"def"},
					{"ghi"},
				},
			},

			{
				Query:    "select * from vt where v = 'defdef';",
				Expected: []sql.Row{},
			},
			{
				Query: "select * from vt where v < 'defdef';",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select * from vt where v > 'defdef';",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: "select * from vt where v <= 'defdef';",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select * from vt where v >= 'defdef';",
				Expected: []sql.Row{
					{"ghi"},
				},
			},

			// MySQL behavior around null bytes is strange
			{
				Skip:  true,
				Query: `select * from vt where v = 'def\0\0';`,
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Skip:  true,
				Query: `select * from vt where v < 'def\0\0';`,
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: `select * from vt where v > 'def\0\0';`,
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: `select * from vt where v <= 'def\0\0';`,
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Skip:  true,
				Query: `select * from vt where v >= 'def\0\0';`,
				Expected: []sql.Row{
					{"def"},
					{"ghi"},
				},
			},

			{
				Query: "select * from vt where v = cast('def' as char(6));",
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Query: "select * from vt where v < cast('def' as char(6));",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select * from vt where v > cast('def' as char(6));",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: "select * from vt where v <= cast('def' as char(6));",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select * from vt where v >= cast('def' as char(6));",
				Expected: []sql.Row{
					{"def"},
					{"ghi"},
				},
			},
		},
	},
	{
		Name: "varbinary primary key",
		SetUpScript: []string{
			"create table vt (v varbinary(3) primary key);",
			"insert into vt values ('abc'), ('def'), ('ghi');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select cast(v as char(3)) from vt where v = 'def';",
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v < 'def';",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v > 'def';",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v <= 'def';",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v >= 'def';",
				Expected: []sql.Row{
					{"def"},
					{"ghi"},
				},
			},

			{
				Query:    "select cast(v as char(3)) from vt where v = 'defdef';",
				Expected: []sql.Row{},
			},
			{
				Query: "select cast(v as char(3)) from vt where v < 'defdef';",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v > 'defdef';",
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v <= 'defdef';",
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Query: "select cast(v as char(3)) from vt where v >= 'defdef';",
				Expected: []sql.Row{
					{"ghi"},
				},
			},

			// MySQL behavior around null bytes is strange
			{
				Skip:  true,
				Query: `select cast(v as char(3)) from vt where v = 'def\0\0';`,
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Skip:  true,
				Query: `select cast(v as char(3)) from vt where v < 'def\0\0';`,
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: `select cast(v as char(3)) from vt where v > 'def\0\0';`,
				Expected: []sql.Row{
					{"ghi"},
				},
			},
			{
				Query: `select cast(v as char(3)) from vt where v <= 'def\0\0';`,
				Expected: []sql.Row{
					{"abc"},
					{"def"},
				},
			},
			{
				Skip:  true,
				Query: `select cast(v as char(3)) from vt where v >= 'def\0\0';`,
				Expected: []sql.Row{
					{"def"},
					{"ghi"},
				},
			},
		},
	},
	{
		Name: "primary key order",
		SetUpScript: []string{
			"create table t1 (a varchar(5), b varchar(10), primary key(a, b));",
			"create table t2 (a varchar(5), b varchar(10), primary key(b, a));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "insert into t1 (a, b) values ('1234567890', '12345')",
				ExpectedErrStr: "string '1234567890' is too large for column 'a'",
			},
			{
				Query: "insert into t1 (b, a) values ('1234567890', '12345')",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "select a, b from t1",
				Expected: []sql.Row{
					{"12345", "1234567890"},
				},
			},
			{
				Query:          "insert into t2 (a, b) values ('1234567890', '12345')",
				ExpectedErrStr: "string '1234567890' is too large for column 'a'",
			},
			{
				Query: "insert into t2 (b, a) values ('1234567890', '12345')",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "select a, b from t2",
				Expected: []sql.Row{
					{"12345", "1234567890"},
				},
			},
		},
	},
	{
		Name: "test json search",
		SetUpScript: []string{
			`create table t (i int primary key, j json);`,
			`insert into t values (0, '{"a": "abc"}'), (1, '{"b": "abc"}'), (2, '{"c": "abc"}');`,
			`insert into t values (3, '{"d": "def"}'), (4, '{"e": "def"}'), (5, '{"f": "def"}');`,
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select i, json_search(j, 'all', 'abc') from t order by i",
				Expected: []sql.Row{
					{0, types.MustJSON(`"$.a"`)},
					{1, types.MustJSON(`"$.b"`)},
					{2, types.MustJSON(`"$.c"`)},
					{3, nil},
					{4, nil},
					{5, nil},
				},
			},
			{
				Query: "select i, json_search(j, 'all', 'def') from t order by i",
				Expected: []sql.Row{
					{0, nil},
					{1, nil},
					{2, nil},
					{3, types.MustJSON(`"$.d"`)},
					{4, types.MustJSON(`"$.e"`)},
					{5, types.MustJSON(`"$.f"`)},
				},
			},
			{
				Query: "select i, json_search(j, 'all', 'abc', '', '$.a', '$.b') from t order by i",
				Expected: []sql.Row{
					{0, types.MustJSON(`"$.a"`)},
					{1, types.MustJSON(`"$.b"`)},
					{2, nil},
					{3, nil},
					{4, nil},
					{5, nil},
				},
			},
		},
	},
	{
		Name:    "test show create database",
		Dialect: "mysql",
		SetUpScript: []string{
			"create database def_db;",
			"create database latin1_db character set latin1;",
			"create database bin_db charset binary;",
			"create database mb3_db collate utf8mb3_general_ci;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create database def_db",
				Expected: []sql.Row{
					{"def_db", "CREATE DATABASE `def_db` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin */"},
				},
			},
			{
				Query: "show create database latin1_db",
				Expected: []sql.Row{
					{"latin1_db", "CREATE DATABASE `latin1_db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */"},
				},
			},
			{
				Query: "show create database bin_db",
				Expected: []sql.Row{
					{"bin_db", "CREATE DATABASE `bin_db` /*!40100 DEFAULT CHARACTER SET binary COLLATE binary */"},
				},
			},
			{
				Query: "show create database mb3_db",
				Expected: []sql.Row{
					{"mb3_db", "CREATE DATABASE `mb3_db` /*!40100 DEFAULT CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci */"},
				},
			},
		},
	},
	{
		Name:    "test create database with modified server variables",
		Dialect: "mysql",
		SetUpScript: []string{
			"set @@session.character_set_server = 'latin1';",
			"create database latin1_db;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select @@global.character_set_server, @@global.collation_server;",
				Expected: []sql.Row{
					{"utf8mb4", "utf8mb4_0900_bin"},
				},
			},
			{
				Query: "select @@session.character_set_server, @@session.collation_server;",
				Expected: []sql.Row{
					{"latin1", "latin1_swedish_ci"},
				},
			},
			{
				// Interestingly, session actually takes priority over global
				Query: "show create database latin1_db",
				Expected: []sql.Row{
					{"latin1_db", "CREATE DATABASE `latin1_db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */"},
				},
			},
		},
	},
	{
		Name:    "test index naming",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int);",
			"alter table t add index (i);",
			"alter table t add index (i);",
			"alter table t add index (i);",

			"create table tt (i int);",
			"alter table tt add index i_3(i);",
			"alter table tt add index (i);",
			"alter table tt add index (i);",
			"alter table tt add index (i);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t",
				Expected: []sql.Row{
					{"t", "CREATE TABLE `t` (\n" +
						"  `i` int,\n" +
						"  KEY `i` (`i`),\n" +
						"  KEY `i_2` (`i`),\n" +
						"  KEY `i_3` (`i`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				// MySQL preserves the other that indexes are created
				// We store them in a map, so we have to sort to have some consistency
				Query: "show create table tt",
				Expected: []sql.Row{
					{"tt", "CREATE TABLE `tt` (\n" +
						"  `i` int,\n" +
						"  KEY `i` (`i`),\n" +
						"  KEY `i_2` (`i`),\n" +
						"  KEY `i_3` (`i`),\n" +
						"  KEY `i_4` (`i`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},
	{
		Name:    "test parenthesized tables",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t1 (i int);",
			"insert into t1 values (1), (2), (3);",
			"create table t2 (j int);",
			"insert into t2 values (1), (3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from (t1)",
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "select * from (((((t1)))))",
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "select * from (((((t1 as t11)))))",
				Expected: []sql.Row{
					{1},
					{2},
					{3},
				},
			},
			{
				Query: "select * from (t1) join t2 where t1.i = t2.j",
				Expected: []sql.Row{
					{1, 1},
					{3, 3},
				},
			},
			{
				Query: "select * from t1 join (t2) where t1.i = t2.j",
				Expected: []sql.Row{
					{1, 1},
					{3, 3},
				},
			},
			{
				Query: "select * from (t1) join (t2) where t1.i = t2.j",
				Expected: []sql.Row{
					{1, 1},
					{3, 3},
				},
			},
			{
				Query: "select * from ((((t1)))) join ((((t2)))) where t1.i = t2.j",
				Expected: []sql.Row{
					{1, 1},
					{3, 3},
				},
			},
			{
				Query: "select * from (t1 as t11) join (t2 as t22) where t11.i = t22.j",
				Expected: []sql.Row{
					{1, 1},
					{3, 3},
				},
			},
		},
	},
	{
		Name: "invalid utf8 encoding strings",
		SetUpScript: []string{
			"create table t (c char(10), v varchar(10), txt text, b blob, bi binary(10));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "insert into t(c) values (X'9876543210');",
				ExpectedErrStr: "Incorrect string value: '\\x98vT2\\x10' for column 'c' at row 1",
			},
			{
				Query:          "insert into t(v) values (X'9876543210');",
				ExpectedErrStr: "Incorrect string value: '\\x98vT2\\x10' for column 'v' at row 1",
			},
			{
				Query:          "insert into t(txt) values (X'9876543210');",
				ExpectedErrStr: "Incorrect string value: '\\x98vT2\\x10' for column 'txt' at row 1",
			},
			{
				Query: "insert into t(b) values (X'9876543210');",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "insert into t(bi) values (X'9876543210');",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
		},
	},
	{
		Name:    "charset validation strict vs non-strict mode",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table charset_test (c char(10), v varchar(10), txt text) character set utf8mb4;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "set sql_mode = 'STRICT_TRANS_TABLES';",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:          "insert into charset_test(c) values (UNHEX('446F6C744C6162AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_test(v) values (UNHEX('446F6C744C6162AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'v' at row 1",
			},
			{
				Query:          "insert into charset_test(txt) values (UNHEX('446F6C744C6162AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'txt' at row 1",
			},
			{
				Query:    "set sql_mode = '';",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query: "insert into charset_test(c) values (UNHEX('446F6C744C6162AE'));",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "insert into charset_test(v) values (UNHEX('446F6C744C6162AE'));",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "insert into charset_test(txt) values (UNHEX('446F6C744C6162AE'));",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "select HEX(c), LENGTH(c) from charset_test where c is not null;",
				Expected: []sql.Row{
					{"446F6C744C6162", 7},
				},
			},
			{
				Query: "select HEX(v), LENGTH(v) from charset_test where v is not null;",
				Expected: []sql.Row{
					{"446F6C744C6162", 7},
				},
			},
			{
				Query: "select HEX(txt), LENGTH(txt) from charset_test where txt is not null;",
				Expected: []sql.Row{
					{"446F6C744C6162", 7},
				},
			},
		},
	},
	{
		Name:    "charset validation issue #8893 - customer scenario",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table products (id int primary key, name text character set utf8mb4);",
		},
		Assertions: []ScriptTestAssertion{
			// Test charset validation with invalid UTF-8 data
			{
				Query:          "insert into products values (1, UNHEX('446F6C744C6162AE'));", // "DoltLab" + invalid byte 0xAE
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'name' at row 1",
			},
			// Test non-strict mode truncation behavior
			{
				Query:    "set sql_mode = '';",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "insert into products values (1, UNHEX('446F6C744C6162AE'));", // Now succeeds with truncation
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			// Verify data was truncated at invalid byte (MySQL behavior)
			{
				Query: "select id, name, HEX(name) from products;",
				Expected: []sql.Row{
					{1, "DoltLab", "446F6C744C6162"}, // Invalid byte 0xAE was truncated
				},
			},
			// Customer can now query and work with the data
			{
				Query: "select id, name from products where name like '%Lab%';",
				Expected: []sql.Row{
					{1, "DoltLab"},
				},
			},
		},
	},
	{
		Name:    "charset validation edge cases - formatInvalidByteForError testing",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table charset_edge_test (c char(10), v varchar(20), t text);",
		},
		Assertions: []ScriptTestAssertion{
			// STRICT MODE TESTS
			{
				Query:    "set sql_mode = 'STRICT_TRANS_TABLES';",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			// Single invalid byte (0xAE)
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(v) values (UNHEX('AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'v' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(t) values (UNHEX('AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 't' at row 1",
			},
			// Multiple invalid bytes
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('AEAEAE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE\\xAE\\xAE' for column 'c' at row 1",
			},
			// Overlong sequences
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('C0C1'));",
				ExpectedErrStr: "Incorrect string value: '\\xC0\\xC1' for column 'c' at row 1",
			},
			// Invalid bytes 0xFE, 0xFF
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('FE'));",
				ExpectedErrStr: "Incorrect string value: '\\xFE' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('FF'));",
				ExpectedErrStr: "Incorrect string value: '\\xFF' for column 'c' at row 1",
			},
			// Surrogate pairs
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('EDA080'));",
				ExpectedErrStr: "Incorrect string value: '\\xED\\xA0\\x80' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('EDBFBF'));",
				ExpectedErrStr: "Incorrect string value: '\\xED\\xBF\\xBF' for column 'c' at row 1",
			},
			// More overlong sequences
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('C080'));",
				ExpectedErrStr: "Incorrect string value: '\\xC0\\x80' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('E08080'));",
				ExpectedErrStr: "Incorrect string value: '\\xE0\\x80\\x80' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('F0808080'));",
				ExpectedErrStr: "Incorrect string value: '\\xF0\\x80\\x80\\x80' for column 'c' at row 1",
			},
			// Out of range (beyond U+10FFFF)
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('F4908080'));",
				ExpectedErrStr: "Incorrect string value: '\\xF4\\x90\\x80\\x80' for column 'c' at row 1",
			},
			// Continuation bytes without start byte
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('80'));",
				ExpectedErrStr: "Incorrect string value: '\\x80' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('BF'));",
				ExpectedErrStr: "Incorrect string value: '\\xBF' for column 'c' at row 1",
			},
			// Incomplete sequences
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('C2'));",
				ExpectedErrStr: "Incorrect string value: '\\xC2' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('E0A0'));",
				ExpectedErrStr: "Incorrect string value: '\\xE0\\xA0' for column 'c' at row 1",
			},
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('F09080'));",
				ExpectedErrStr: "Incorrect string value: '\\xF0\\x90\\x80' for column 'c' at row 1",
			},
			// Long sequence (tests truncation with ...)
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('999897969594939291'));",
				ExpectedErrStr: "Incorrect string value: '\\x99\\x98\\x97\\x96\\x95\\x94...' for column 'c' at row 1",
			},
			// Valid UTF-8 with invalid bytes
			{
				Query:          "insert into charset_edge_test(c) values (UNHEX('446F6C744C6162AE'));",
				ExpectedErrStr: "Incorrect string value: '\\xAE' for column 'c' at row 1",
			},

			// NON-STRICT MODE TESTS (should truncate)
			{
				Query:    "set sql_mode = '';",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "insert into charset_edge_test(c) values (UNHEX('446F6C744C6162AE'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into charset_edge_test(v) values (UNHEX('48656C6C6FC0'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into charset_edge_test(t) values (UNHEX('54657374FF'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			// Verify truncated data
			{
				Query: "select HEX(c), LENGTH(c) from charset_edge_test where c is not null;",
				Expected: []sql.Row{
					{"446F6C744C6162", 7},
				},
			},
			{
				Query: "select HEX(v), LENGTH(v) from charset_edge_test where v is not null;",
				Expected: []sql.Row{
					{"48656C6C6F", 5},
				},
			},
			{
				Query: "select HEX(t), LENGTH(t) from charset_edge_test where t is not null;",
				Expected: []sql.Row{
					{"54657374", 4},
				},
			},
		},
	},
	{
		Name:    "charset validation ASCII range tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table ascii_test (c char(10), v varchar(20), t text);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "set sql_mode = 'STRICT_TRANS_TABLES';",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			// ASCII range 0x00-0x7F
			{
				Query:    "insert into ascii_test(c) values (UNHEX('00'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('20'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('41'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('7F'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(v) values (UNHEX('48656C6C6F'));", // "Hello"
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(t) values (UNHEX('00207F41'));",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			// Verify ASCII data
			{
				Query: "select HEX(c), LENGTH(c) from ascii_test where c is not null order by c;",
				Expected: []sql.Row{
					{"00", 1},
					{"20", 1},
					{"41", 1},
					{"7F", 1},
				},
			},
			{
				Query: "select HEX(v), LENGTH(v) from ascii_test where v is not null;",
				Expected: []sql.Row{
					{"48656C6C6F", 5}, // "Hello"
				},
			},
			{
				Query: "select HEX(t), LENGTH(t) from ascii_test where t is not null;",
				Expected: []sql.Row{
					{"00207F41", 4}, // NULL + SPACE + DEL + A
				},
			},
			// Boundary cases
			{
				Query:    "insert into ascii_test(c) values (UNHEX('7E'));", // 0x7E is valid ASCII
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:          "insert into ascii_test(c) values (UNHEX('81'));", // 0x81 is invalid
				ExpectedErrStr: "Incorrect string value: '\\x81' for column 'c' at row 1",
			},
			// Mixed ASCII and invalid (non-strict mode)
			{
				Query:    "set sql_mode = '';", // Non-strict mode
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('41424380'));", // ABC + 0x80 (invalid)
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			// Verify truncation
			{
				Query: "select HEX(c), LENGTH(c) from ascii_test where HEX(c) = '414243';",
				Expected: []sql.Row{
					{"414243", 3}, // "ABC" - truncated at invalid byte
				},
			},
			// Valid UTF-8 sequences
			{
				Query:    "set sql_mode = 'STRICT_TRANS_TABLES';", // Back to strict mode
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('C3A9'));", // é (2-byte UTF-8)
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('E282AC'));", // € (3-byte UTF-8)
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "insert into ascii_test(c) values (UNHEX('F09D849E'));", // 𝄞 (4-byte UTF-8)
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			// Function boundary constants (asciiMin=32, asciiMax=127)
			{
				Query:    "insert into ascii_test(c) values (UNHEX('1F'));", // ASCII 31 (below asciiMin=32) - valid ASCII but non-printable
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			// Note: UNHEX('80') test is covered in edge cases test above
		},
	},
	{
		Name:    "unix_timestamp script tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"set time_zone = 'UTC';",
			"create table t1 (i int primary key, v varchar(100));",
			"insert into t1 values (0, '2000-01-01 12:34:56');",
			"insert into t1 values (1, '2000-01-01 12:34:56.1');",
			"insert into t1 values (2, '2000-01-01 12:34:56.12');",
			"insert into t1 values (3, '2000-01-01 12:34:56.123');",
			"insert into t1 values (4, '2000-01-01 12:34:56.1234');",
			"insert into t1 values (5, '2000-01-01 12:34:56.12345');",
			"insert into t1 values (6, '2000-01-01 12:34:56.123456');",
		},
		Assertions: []ScriptTestAssertion{
			{
				// TODO: server engine is not respecting timezone
				SkipResultCheckOnServerEngine: true,
				Query:                         "select i, unix_timestamp(v) from t1",
				Expected: []sql.Row{
					{0, "946730096.000000"},
					{1, "946730096.100000"},
					{2, "946730096.120000"},
					{3, "946730096.123000"},
					{4, "946730096.123400"},
					{5, "946730096.123450"},
					{6, "946730096.123456"},
				},
			},
		},
	},
	{
		Name:    "name_const queries",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key);",
			"insert into t values (1), (2), (3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select name_const(123, 123)",
				ExpectedColumns: sql.Schema{
					{Name: "123", Type: types.Int8, Nullable: false},
				},
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select name_const('abc', 123)",
				ExpectedColumns: sql.Schema{
					{Name: "abc", Type: types.Int8, Nullable: false},
				},
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select name_const('abc', 'abc')",
				ExpectedColumns: sql.Schema{
					{Name: "abc", Type: types.Text, Nullable: false},
				},
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select name_const(date '2000-01-02', 123)",
				ExpectedColumns: sql.Schema{
					{Name: "2000-01-02", Type: types.Int8, Nullable: false},
				},
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select name_const('abc', date '2001-02-03')",
				ExpectedColumns: sql.Schema{
					{Name: "abc", Type: types.Text, Nullable: false},
				},
				Expected: []sql.Row{
					{"2001-02-03"},
				},
			},

			{
				Query:          "select name_const('abc', 1+1)",
				ExpectedErrStr: "incorrect arguments to: NAME_CONST",
			},
			{
				Query:          "select name_const(1+1, 123)",
				ExpectedErrStr: "incorrect arguments to: NAME_CONST",
			},
			{
				Query:          "select name_const(i, 123) from t",
				ExpectedErrStr: "incorrect arguments to: NAME_CONST",
			},
			{
				Query:          "select name_const(123, i) from t",
				ExpectedErrStr: "incorrect arguments to: NAME_CONST",
			},
			{
				Query:          "select name_const()",
				ExpectedErrStr: "incorrect parameter count in the call to native function NAME_CONST",
			},
			{
				Query:          "select name_const(1)",
				ExpectedErrStr: "incorrect parameter count in the call to native function NAME_CONST",
			},
			{
				Query:          "select name_const(1, 2, 3)",
				ExpectedErrStr: "incorrect parameter count in the call to native function NAME_CONST",
			},
		},
	},
	{
		Name: "mismatched collation using hash in tuples",
		SetUpScript: []string{
			"create table t (t1 text collate utf8mb4_0900_bin, t2 text collate utf8mb4_0900_ai_ci)",
			"insert into t values ('ABC', 'DEF')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t where (t1, t2) in (('ABC', 'DEF'));",
				Expected: []sql.Row{
					{"ABC", "DEF"},
				},
			},
			{
				Query: "select * from t where (t1, t2) in (('ABC', 'def'));",
				Expected: []sql.Row{
					{"ABC", "DEF"},
				},
			},
			{
				Query:    "select * from t where (t1, t2) in (('abc', 'DEF'));",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "not null not unique index works on server engine",
		SetUpScript: []string{
			"create table t (i int not null, index (i));",
			"insert into t values (1), (1), (1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t where i = 1;",
				Expected: []sql.Row{
					{1},
					{1},
					{1},
				},
			},
		},
	},
	{
		Name: "validate_password_strength and validate_password.length",
		SetUpScript: []string{
			"set @orig = @@global.validate_password.length",
			"set @@global.validate_password.length = 0",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select validate_password_strength('')",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: "select validate_password_strength('123')",
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: "select validate_password_strength('1234')",
				Expected: []sql.Row{
					{50},
				},
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.length = 1000",
			},
			{
				Query: "select validate_password_strength('ABCabc123!@!#')",
				Expected: []sql.Row{
					{25},
				},
			},
			{
				Query:          "set @@session.validate_password.length = 123",
				ExpectedErrStr: "Variable 'validate_password.length' is a GLOBAL variable and should be set with SET GLOBAL",
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.length = @orig",
			},
		},
	},
	{
		Name: "validate_password_strength and validate_password.number_count",
		SetUpScript: []string{
			"set @orig = @@global.validate_password.number_count",
			"set @@global.validate_password.number_count = 0",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select validate_password_strength('ABCabc!@#')",
				Expected: []sql.Row{
					{100},
				},
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.number_count = 1000",
			},
			{
				Query: "select validate_password_strength('ABCabc!!!!123456789012345678901234567890')",
				Expected: []sql.Row{
					{50},
				},
			},
			{
				Query:          "set @@session.validate_password.number_count = 123",
				ExpectedErrStr: "Variable 'validate_password.number_count' is a GLOBAL variable and should be set with SET GLOBAL",
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.number_count = @orig",
			},
		},
	},
	{
		Name: "validate_password_strength and validate_password.mixed_case_count",
		SetUpScript: []string{
			"set @orig = @@global.validate_password.mixed_case_count",
			"set @@global.validate_password.mixed_case_count = 0",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select validate_password_strength('abcabc!@#123')",
				Expected: []sql.Row{
					{100},
				},
			},
			{
				Query: "select validate_password_strength('ABCABC!@#123')",
				Expected: []sql.Row{
					{100},
				},
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.mixed_case_count = 1000",
			},
			{
				Query: "select validate_password_strength('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456!?!?!?')",
				Expected: []sql.Row{
					{50},
				},
			},
			{
				Query:          "set @@session.validate_password.mixed_case_count = 123",
				ExpectedErrStr: "Variable 'validate_password.mixed_case_count' is a GLOBAL variable and should be set with SET GLOBAL",
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.mixed_case_count = @orig",
			},
		},
	},
	{
		Name: "validate_password_strength and validate_password.special_char_count",
		SetUpScript: []string{
			"set @orig = @@global.validate_password.special_char_count",
			"set @@global.validate_password.special_char_count = 0",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select validate_password_strength('abcABC123')",
				Expected: []sql.Row{
					{100},
				},
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.special_char_count = 1000",
			},
			{
				Query: "select validate_password_strength('abcABC123!@#$%^&*()                            ')",
				Expected: []sql.Row{
					{50},
				},
			},
			{
				Query:          "set @@session.validate_password.special_char_count = 123",
				ExpectedErrStr: "Variable 'validate_password.special_char_count' is a GLOBAL variable and should be set with SET GLOBAL",
			},
			{
				SkipResultsCheck: true,
				Query:            "set @@global.validate_password.special_char_count = @orig",
			},
		},
	},
	{
		Name: "coalesce with system types",
		SetUpScript: []string{
			"create table t as select @@admin_port as port1, @@port as port2, COALESCE(@@admin_port, @@port) as\n port3;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "describe t;",
				Expected: []sql.Row{
					{"port1", "bigint", "NO", "", nil, ""},
					{"port2", "bigint", "NO", "", nil, ""},
					{"port3", "bigint", "NO", "", nil, ""},
				},
			},
		},
	},

	{
		Name:    "not expression optimization",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int);",
			"insert into t values (123);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select * from t where 1 = (not(not(i)))",
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select * from t where true = (not(not(i)))",
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select * from t where true = (not(not(i = 123)))",
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select * from t where false = (not(not(i != 123)))",
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select * from t where i != (false or i);",
				Expected: []sql.Row{
					{123},
				},
			},
			{
				Query: "select * from t where ((true and -1) >= 0);",
				Expected: []sql.Row{
					{123},
				},
			},
		},
	},
	{
		Name:    "negative int limits",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t(i8 tinyint, i16 smallint, i24 mediumint, i32 int, i64 bigint);",
			"INSERT INTO t VALUES(-128, -32768, -8388608, -2147483648, -9223372036854775808);",
		},
		Assertions: []ScriptTestAssertion{
			{
				SkipResultCheckOnServerEngine: true,
				Query:                         "SELECT -i8, -i16, -i24, -i32 from t;",
				Expected: []sql.Row{
					{128, 32768, 8388608, 2147483648},
				},
			},
			{
				Query:          "SELECT -i64 from t;",
				ExpectedErrStr: "BIGINT out of range for -9223372036854775808",
			},
		},
	},
	{
		Name:    "negative int limits",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t(i8 tinyint, i16 smallint, i24 mediumint, i32 int, i64 bigint);",
			"INSERT INTO t VALUES(-128, -32768, -8388608, -2147483648, -9223372036854775808);",
		},
		Assertions: []ScriptTestAssertion{
			{
				SkipResultCheckOnServerEngine: true,
				Query:                         "SELECT -i8, -i16, -i24, -i32 from t;",
				Expected: []sql.Row{
					{128, 32768, 8388608, 2147483648},
				},
			},
			{
				Query:          "SELECT -i64 from t;",
				ExpectedErrStr: "BIGINT out of range for -9223372036854775808",
			},
		},
	},
	{
		Name:    "std, stdev, stddev_pop, variance, var_pop, var_samp tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int);",
			"create table tt (i int, j int);",
			"insert into tt values (0, 1), (0, 2), (0, 3);",
			"insert into tt values (1, 123), (1, 456), (1, 789);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select std(i), stddev(i), stddev_pop(i), stddev_samp(i) from t;",
				Expected: []sql.Row{
					{nil, nil, nil, nil},
				},
			},
			{
				Query: "select variance(i), var_pop(i), var_samp(i) from t;",
				Expected: []sql.Row{
					{nil, nil, nil},
				},
			},
			{
				Query: "insert into t values (1);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select std(i), stddev(i), stddev_pop(i), stddev_samp(i) from t;",
				Expected: []sql.Row{
					{0.0, 0.0, 0.0, nil},
				},
			},
			{
				Query: "select variance(i), var_pop(i), var_samp(i) from t;",
				Expected: []sql.Row{
					{0.0, 0.0, nil},
				},
			},
			{
				Query: "insert into t values (2);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select std(i), stddev(i), stddev_pop(i), stddev_samp(i) from t;",
				Expected: []sql.Row{
					{0.5, 0.5, 0.5, 0.7071067811865476},
				},
			},
			{
				Query: "select variance(i), var_pop(i), var_samp(i) from t;",
				Expected: []sql.Row{
					{0.25, 0.25, 0.5},
				},
			},
			{
				Query: "insert into t values (3);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select std(i), stddev(i), stddev_pop(i), stddev_samp(i) from t;",
				Expected: []sql.Row{
					{0.816496580927726, 0.816496580927726, 0.816496580927726, 1.0},
				},
			},
			{
				Query: "select variance(i), var_pop(i), var_samp(i) from t;",
				Expected: []sql.Row{
					{0.6666666666666666, 0.6666666666666666, 1.0},
				},
			},
			{
				Query: "insert into t values (null), (null);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query: "select std(i), stddev(i), stddev_pop(i), stddev_samp(i) from t;",
				Expected: []sql.Row{
					{0.816496580927726, 0.816496580927726, 0.816496580927726, 1.0},
				},
			},
			{
				Query: "select variance(i), var_pop(i), var_samp(i) from t;",
				Expected: []sql.Row{
					{0.6666666666666666, 0.6666666666666666, 1.0},
				},
			},
			{
				Query: "select i, std(j), stddev_samp(j) from tt group by i;",
				Expected: []sql.Row{
					{0, 0.816496580927726, 1.0},
					{1, 271.89336144893275, 333.0},
				},
			},
			{
				Query: "select i, variance(i), var_samp(i) from tt group by i;",
				Expected: []sql.Row{
					{0, 0.0, 0.0},
					{1, 0.0, 0.0},
				},
			},
			{
				Query: "select std(i) over(), std(j) over(), stddev_samp(j) over() from tt order by i;",
				Expected: []sql.Row{
					{0.5, 297.47660972475353, 325.86929895281634},
					{0.5, 297.47660972475353, 325.86929895281634},
					{0.5, 297.47660972475353, 325.86929895281634},
					{0.5, 297.47660972475353, 325.86929895281634},
					{0.5, 297.47660972475353, 325.86929895281634},
					{0.5, 297.47660972475353, 325.86929895281634},
				},
			},
			{
				Query: "select i, std(j) over(partition by i), stddev_samp(j) over(partition by i) from tt order by i;",
				Expected: []sql.Row{
					{0, 0.816496580927726, 1.0},
					{0, 0.816496580927726, 1.0},
					{0, 0.816496580927726, 1.0},
					{1, 271.89336144893275, 333.0},
					{1, 271.89336144893275, 333.0},
					{1, 271.89336144893275, 333.0},
				},
			},
			{
				Query: "select i, variance(i) over(), var_samp(i) over() from tt order by i;",
				Expected: []sql.Row{
					{0, 0.25, 0.3},
					{0, 0.25, 0.3},
					{0, 0.25, 0.3},
					{1, 0.25, 0.3},
					{1, 0.25, 0.3},
					{1, 0.25, 0.3},
				},
			},
			{
				Query: "select i, variance(j) over(partition by i), var_samp(i) over(partition by i) from tt order by i;",
				Expected: []sql.Row{
					{0, 0.6666666666666666, 0.0},
					{0, 0.6666666666666666, 0.0},
					{0, 0.6666666666666666, 0.0},
					{1, 73926.0, 0.0},
					{1, 73926.0, 0.0},
					{1, 73926.0, 0.0},
				},
			},
			{
				Query: "insert into tt values (null, null);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select i, std(i) over(), std(j) over(), stddev_samp(j) over() from tt order by i;",
				Expected: []sql.Row{
					{nil, 0.5, 297.47660972475353, 325.86929895281634},
					{0, 0.5, 297.47660972475353, 325.86929895281634},
					{0, 0.5, 297.47660972475353, 325.86929895281634},
					{0, 0.5, 297.47660972475353, 325.86929895281634},
					{1, 0.5, 297.47660972475353, 325.86929895281634},
					{1, 0.5, 297.47660972475353, 325.86929895281634},
					{1, 0.5, 297.47660972475353, 325.86929895281634},
				},
			},
			{
				Query: "select i, std(j) over(partition by i), stddev_samp(j) over(partition by i) from tt order by i;",
				Expected: []sql.Row{
					{nil, nil, nil},
					{0, 0.816496580927726, 1.0},
					{0, 0.816496580927726, 1.0},
					{0, 0.816496580927726, 1.0},
					{1, 271.89336144893275, 333.0},
					{1, 271.89336144893275, 333.0},
					{1, 271.89336144893275, 333.0},
				},
			},
			{
				Query: "select i, variance(i) over(), var_samp(i) over() from tt order by i;",
				Expected: []sql.Row{
					{nil, 0.25, 0.3},
					{0, 0.25, 0.3},
					{0, 0.25, 0.3},
					{0, 0.25, 0.3},
					{1, 0.25, 0.3},
					{1, 0.25, 0.3},
					{1, 0.25, 0.3},
				},
			},
			{
				Query: "select i, variance(j) over(partition by i), var_samp(i) over(partition by i) from tt order by i;",
				Expected: []sql.Row{
					{nil, nil, nil},
					{0, 0.6666666666666666, 0.0},
					{0, 0.6666666666666666, 0.0},
					{0, 0.6666666666666666, 0.0},
					{1, 73926.0, 0.0},
					{1, 73926.0, 0.0},
					{1, 73926.0, 0.0},
				},
			},
			{
				Query: "select i, stddev_pop(j) over w, stddev_samp(j) over w, variance(j) over w, var_samp(i) over w from tt window w as (partition by i) order by i;",
				Expected: []sql.Row{
					{nil, nil, nil, nil, nil},
					{0, 0.816496580927726, 1.0, 0.6666666666666666, 0.0},
					{0, 0.816496580927726, 1.0, 0.6666666666666666, 0.0},
					{0, 0.816496580927726, 1.0, 0.6666666666666666, 0.0},
					{1, 271.89336144893275, 333.0, 73926.0, 0.0},
					{1, 271.89336144893275, 333.0, 73926.0, 0.0},
					{1, 271.89336144893275, 333.0, 73926.0, 0.0},
				},
			},
		},
	},
	{
		Name:    "ntile tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, j int);",
			"insert into t values (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 2), (7, 2), (8, 2), (9, 2), (10, 2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "select i, ntile(null) over() from t;",
				ExpectedErr: sql.ErrInvalidArgument,
			},
			{
				Query:       "select i, ntile(0) over() from t;",
				ExpectedErr: sql.ErrInvalidArgument,
			},
			{
				Query:       "select i, ntile(-1) over() from t;",
				ExpectedErr: sql.ErrInvalidArgument,
			},
			{
				Query: "select i, ntile(100) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(2)},
					{3, uint64(3)},
					{4, uint64(4)},
					{5, uint64(5)},
					{6, uint64(6)},
					{7, uint64(7)},
					{8, uint64(8)},
					{9, uint64(9)},
					{10, uint64(10)},
				},
			},
			{
				Query: "select i, ntile(10) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(2)},
					{3, uint64(3)},
					{4, uint64(4)},
					{5, uint64(5)},
					{6, uint64(6)},
					{7, uint64(7)},
					{8, uint64(8)},
					{9, uint64(9)},
					{10, uint64(10)},
				},
			},
			{
				Query: "select i, ntile(9) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(2)},
					{4, uint64(3)},
					{5, uint64(4)},
					{6, uint64(5)},
					{7, uint64(6)},
					{8, uint64(7)},
					{9, uint64(8)},
					{10, uint64(9)},
				},
			},
			{
				Query: "select i, ntile(8) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(2)},
					{4, uint64(2)},
					{5, uint64(3)},
					{6, uint64(4)},
					{7, uint64(5)},
					{8, uint64(6)},
					{9, uint64(7)},
					{10, uint64(8)},
				},
			},
			{
				Query: "select i, ntile(7) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(2)},
					{4, uint64(2)},
					{5, uint64(3)},
					{6, uint64(3)},
					{7, uint64(4)},
					{8, uint64(5)},
					{9, uint64(6)},
					{10, uint64(7)},
				},
			},
			{
				Query: "select i, ntile(6) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(2)},
					{4, uint64(2)},
					{5, uint64(3)},
					{6, uint64(3)},
					{7, uint64(4)},
					{8, uint64(4)},
					{9, uint64(5)},
					{10, uint64(6)},
				},
			},
			{
				Query: "select i, ntile(5) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(2)},
					{4, uint64(2)},
					{5, uint64(3)},
					{6, uint64(3)},
					{7, uint64(4)},
					{8, uint64(4)},
					{9, uint64(5)},
					{10, uint64(5)},
				},
			},
			{
				Query: "select i, ntile(4) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(1)},
					{4, uint64(2)},
					{5, uint64(2)},
					{6, uint64(2)},
					{7, uint64(3)},
					{8, uint64(3)},
					{9, uint64(4)},
					{10, uint64(4)},
				},
			},
			{
				Query: "select i, ntile(3) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(1)},
					{4, uint64(1)},
					{5, uint64(2)},
					{6, uint64(2)},
					{7, uint64(2)},
					{8, uint64(3)},
					{9, uint64(3)},
					{10, uint64(3)},
				},
			},
			{
				Query: "select i, ntile(2) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(1)},
					{4, uint64(1)},
					{5, uint64(1)},
					{6, uint64(2)},
					{7, uint64(2)},
					{8, uint64(2)},
					{9, uint64(2)},
					{10, uint64(2)},
				},
			},
			{
				Query: "select i, ntile(1) over() from t;",
				Expected: []sql.Row{
					{1, uint64(1)},
					{2, uint64(1)},
					{3, uint64(1)},
					{4, uint64(1)},
					{5, uint64(1)},
					{6, uint64(1)},
					{7, uint64(1)},
					{8, uint64(1)},
					{9, uint64(1)},
					{10, uint64(1)},
				},
			},
			{
				Query: "select i, j, ntile(2) over(partition by j) from t;",
				Expected: []sql.Row{
					{1, 1, uint64(1)},
					{2, 1, uint64(1)},
					{3, 1, uint64(1)},
					{4, 1, uint64(2)},
					{5, 1, uint64(2)},
					{6, 2, uint64(1)},
					{7, 2, uint64(1)},
					{8, 2, uint64(1)},
					{9, 2, uint64(2)},
					{10, 2, uint64(2)},
				},
			},
		},
	},
	{
		Name:    "bit default value",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, b bit(2) default 2);",
			"insert into t(i) values (1);",
			"create table tt (b bit(2) default 2 primary key);",
			"insert into tt values ();",
		},
		Assertions: []ScriptTestAssertion{
			{
				Skip:  true, // this fails on server engine, even when skipped
				Query: "select * from t;",
				Expected: []sql.Row{
					{1, uint8(2)},
				},
			},
			{
				Skip:  true, // this fails on server engine, even when skipped
				Query: "select * from tt;",
				Expected: []sql.Row{
					{uint8(2)},
				},
			},
		},
	},
	{
		Name:    "hash tuples",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (id longtext);",
			"INSERT INTO test (id) VALUES ('test_id');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "SELECT * FROM test WHERE id IN ('test_id');",
				Expected: []sql.Row{
					{"test_id"},
				},
			},
		},
	},
	{
		// This is a script test here because every table in the harness setup data is in all lowercase
		Name:    "case insensitive update with insubqueries and update joins",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table MiXeDcAsE (i int primary key, j int)",
			"insert into mixedcase values (1, 1);",
			"insert into mixedcase values (2, 2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "update mixedcase set j = 999 where i in (select 1)",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						Info: plan.UpdateInfo{
							Matched: 1,
							Updated: 1,
						},
					}},
				},
			},
			{
				Query: "select * from mixedcase;",
				Expected: []sql.Row{
					{1, 999},
					{2, 2},
				},
			},
			{
				Query: " with cte(x) as (select 2) update mixedcase set j = 999 where i in (select x from cte)",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						Info: plan.UpdateInfo{
							Matched: 1,
							Updated: 1,
						},
					}},
				},
			},
			{
				Query: "select * from mixedcase;",
				Expected: []sql.Row{
					{1, 999},
					{2, 999},
				},
			},
		},
	},
	{
		Name:    "substring function tests with wrappers",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table tbl (t text);",
			"insert into tbl values ('abcdef');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select left(t, 3) from tbl;",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select right(t, 3) from tbl;",
				Expected: []sql.Row{
					{"def"},
				},
			},
			{
				Query: "select instr(t, 'bcd') from tbl;",
				Expected: []sql.Row{
					{2},
				},
			},
		},
	},
	{
		Name: "tinyint column does not restrict IF or IFNULL output",
		// https://github.com/dolthub/dolt/issues/9321
		SetUpScript: []string{
			"create table t0 (c0 tinyint);",
			"insert into t0 values (null);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select ifnull(t0.c0, 128) as ref0 from t0",
				Expected: []sql.Row{
					{128},
				},
			},
			{
				Query:    "select if(t0.c0 = 1, t0.c0, 128) as ref0 from t0",
				Expected: []sql.Row{{128}},
			},
		},
	},
	{
		Name:    "subquery with case insensitive collation",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table tbl (t text) collate=utf8mb4_0900_ai_ci;",
			"insert into tbl values ('abcdef');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select 'AbCdEf' in (select t from tbl);",
				Expected: []sql.Row{
					{true},
				},
			},
		},
	},

	// Char tests
	{
		Name:        "char with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (c char primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'c'",
			},
		},
	},

	// Varchar tests
	{
		Name:        "varchar with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (vc char(100) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'vc'", // We throw the wrong error
			},
		},
	},

	// Binary tests
	{
		Name:        "binary with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (b binary(100) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'b'",
			},
		},
	},

	// Varbinary tests
	{
		Name:        "varbinary with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (vb varbinary(100) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'vb'",
			},
		},
	},

	// Blob tests
	{
		Name:        "blob with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (b blob primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'b'",
			},
			{
				Query:          "create table bad (tb tinyblob primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'tb'",
			},
			{
				Query:          "create table bad (mb mediumblob primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'mb'",
			},
			{
				Query:          "create table bad (lb longblob primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'lb'",
			},
		},
	},

	// Text Tests
	{
		Name:        "text with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (t text primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 't'", // We throw the wrong error
			},
			{
				Query:          "create table bad (tt tinytext primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'tt'", // We throw the wrong error
			},
			{
				Query:          "create table bad (mt mediumtext primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'mt'", // We throw the wrong error
			},
			{
				Query:          "create table bad (lt longtext primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'lt'", // We throw the wrong error
			},
		},
	},

	// Enum tests
	{
		Name:    "enum errors",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, e enum('abc', 'def', 'ghi'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "insert into t values (1, 500)",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query:          "insert into t values (1, -1)",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
		},
	},
	{
		Name:    "enums with default, case-sensitive collation (utf8mb4_0900_bin)",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE enumtest1 (pk int primary key, e enum('abc', 'XYZ'));",
			"CREATE TABLE enumtest2 (pk int PRIMARY KEY, e enum('x ', 'X ', 'y', 'Y'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO enumtest1 VALUES (1, 'abc'), (2, 'abc'), (3, 'XYZ');",
				Expected: []sql.Row{{types.NewOkResult(3)}},
			},
			{
				Query:    "SELECT * FROM enumtest1;",
				Expected: []sql.Row{{1, "abc"}, {2, "abc"}, {3, "XYZ"}},
			},
			{
				// enum values must match EXACTLY for case-sensitive collations
				Query:          "INSERT INTO enumtest1 VALUES (10, 'ABC'), (11, 'aBc'), (12, 'xyz');",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query: "SHOW CREATE TABLE enumtest1;",
				Expected: []sql.Row{{
					"enumtest1",
					"CREATE TABLE `enumtest1` (\n  `pk` int NOT NULL,\n  `e` enum('abc','XYZ'),\n  PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				// Trailing whitespace should be removed from enum values, except when using the "binary" charset and collation
				Query: "SHOW CREATE TABLE enumtest2;",
				Expected: []sql.Row{{
					"enumtest2",
					"CREATE TABLE `enumtest2` (\n  `pk` int NOT NULL,\n  `e` enum('x','X','y','Y'),\n  PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "DESCRIBE enumtest1;",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"e", "enum('abc','XYZ')", "YES", "", nil, ""}},
			},
			{
				Query: "DESCRIBE enumtest2;",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"e", "enum('x','X','y','Y')", "YES", "", nil, ""}},
			},
			{
				Query:    "select data_type, column_type from information_schema.columns where table_name='enumtest1' and column_name='e';",
				Expected: []sql.Row{{"enum", "enum('abc','XYZ')"}},
			},
			{
				Query:    "select data_type, column_type from information_schema.columns where table_name='enumtest2' and column_name='e';",
				Expected: []sql.Row{{"enum", "enum('x','X','y','Y')"}},
			},
		},
	},
	{
		Name:    "enum columns work as expected in when clauses",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table enums (e enum('a'));",
			"insert into enums values ('a');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select (case e when 'a' then 42 end) from enums",
				Expected: []sql.Row{{42}},
			},
			{
				Query:    "select (case 'a' when e then 42 end) from enums",
				Expected: []sql.Row{{42}},
			},
		},
	},
	{
		Name:    "enums with case-insensitive collation (utf8mb4_0900_ai_ci)",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE enumtest1 (pk int primary key, e enum('abc', 'XYZ') collate utf8mb4_0900_ai_ci);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO enumtest1 VALUES (1, 'abc'), (2, 'abc'), (3, 'XYZ');",
				Expected: []sql.Row{{types.NewOkResult(3)}},
			},
			{
				Query: "SHOW CREATE TABLE enumtest1;",
				Expected: []sql.Row{{
					"enumtest1",
					"CREATE TABLE `enumtest1` (\n  `pk` int NOT NULL,\n  `e` enum('abc','XYZ') COLLATE utf8mb4_0900_ai_ci,\n  PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "DESCRIBE enumtest1;",
				Expected: []sql.Row{
					{"pk", "int", "NO", "PRI", nil, ""},
					{"e", "enum('abc','XYZ') COLLATE utf8mb4_0900_ai_ci", "YES", "", nil, ""}},
			},
			{
				Query:    "select data_type, column_type from information_schema.columns where table_name='enumtest1' and column_name='e';",
				Expected: []sql.Row{{"enum", "enum('abc','XYZ')"}},
			},
			{
				Query:    "CREATE TABLE enumtest2 (pk int PRIMARY KEY, e enum('x ', 'X ', 'y', 'Y'));",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "INSERT INTO enumtest1 VALUES (10, 'ABC'), (11, 'aBc'), (12, 'xyz');",
				Expected: []sql.Row{{types.NewOkResult(3)}},
			},
		},
	},
	{
		Name:    "special case for not null default enum",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, e enum('abc', 'def', 'ghi') not null);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t;",
				Expected: []sql.Row{
					{"t", "CREATE TABLE `t` (\n" +
						"  `i` int NOT NULL,\n" +
						"  `e` enum('abc','def','ghi') NOT NULL,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "insert into t(i) values (1)",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:       "insert into t values (2, null)",
				ExpectedErr: sql.ErrInsertIntoNonNullableProvidedNull,
			},
			{
				Skip:  true,
				Query: "insert into t values (2, default)",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t;",
				Expected: []sql.Row{
					{1, "abc"},
				},
			},
		},
	},
	{
		Name:    "ensure that special case does not apply for nullable enums",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, e enum('abc', 'def', 'ghi'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "insert into t(i) values (1)",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t;",
				Expected: []sql.Row{
					{1, nil},
				},
			},
		},
	},
	{
		Name:        "enums with default values",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "create table bad (e enum('a') primary key default null);",
				ExpectedErr: sql.ErrIncompatibleDefaultType,
			},
			{
				Query:       "create table bad (e enum('a') default 0);",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Query:       "create table bad (e enum('a') default '');",
				ExpectedErr: sql.ErrIncompatibleDefaultType,
			},
			{
				Query:       "create table bad (e enum('a') default '1');",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Query:       "create table bad (e enum('a') default 1);",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},

			{
				Query: "create table t1 (e enum('a') default 'a');",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				// TODO: while this is round-trippable, it doesn't match MySQL
				Skip:  true,
				Query: "show create table t1;",
				Expected: []sql.Row{
					{"t1", "CREATE TABLE `t1` (\n" +
						"  `e` enum('a') DEFAULT 'a'\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "insert into t1 values (default);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "insert into t1 values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "insert into t1() values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t1 order by e;",
				Expected: []sql.Row{
					{"a"},
					{"a"},
					{"a"},
				},
			},
			{
				Query: "insert into t1 values (null)",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t1 order by e;",
				Expected: []sql.Row{
					{nil},
					{"a"},
					{"a"},
					{"a"},
				},
			},

			{
				Query: "create table t2 (e enum('a') default (1));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "show create table t2;",
				Expected: []sql.Row{
					{"t2", "CREATE TABLE `t2` (\n" +
						"  `e` enum('a') DEFAULT (1)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "insert into t2 values (default);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "insert into t2 values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "insert into t2() values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t2 order by e;",
				Expected: []sql.Row{
					{"a"},
					{"a"},
					{"a"},
				},
			},
			{
				Query: "insert into t2 values (null)",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t2 order by e;",
				Expected: []sql.Row{
					{nil},
					{"a"},
					{"a"},
					{"a"},
				},
			},

			{
				Query: "create table t3 (e enum('a') default ('1'));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				// TODO: we don't print the collation before the string
				Skip:  true,
				Query: "show create table t3;",
				Expected: []sql.Row{
					{"t3", "CREATE TABLE `t3` (\n" +
						"  `e` enum('a') DEFAULT (_utf8mb4'1')\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "insert into t3 values (default);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "insert into t3 values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "insert into t3() values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t3 order by e;",
				Expected: []sql.Row{
					{"a"},
					{"a"},
					{"a"},
				},
			},
			{
				Query: "insert into t3 values (null)",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t3 order by e;",
				Expected: []sql.Row{
					{nil},
					{"a"},
					{"a"},
					{"a"},
				},
			},
		},
	},
	{
		Name:    "enums with auto increment",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE t (e enum('a', 'b', 'c') PRIMARY KEY)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "CREATE TABLE t2 (e enum('a', 'b', 'c') PRIMARY KEY AUTO_INCREMENT)",
				ExpectedErrStr: "Incorrect column specifier for column 'e'",
			},
			{
				Query:          "ALTER TABLE t MODIFY e enum('a', 'b', 'c') AUTO_INCREMENT",
				ExpectedErrStr: "Incorrect column specifier for column 'e'",
			},
			{
				Query:          "ALTER TABLE t MODIFY COLUMN e enum('a', 'b', 'c') AUTO_INCREMENT",
				ExpectedErrStr: "Incorrect column specifier for column 'e'",
			},
			{
				Query:          "ALTER TABLE t CHANGE e e enum('a', 'b', 'c') AUTO_INCREMENT",
				ExpectedErrStr: "Incorrect column specifier for column 'e'",
			},
			{
				Query:          "ALTER TABLE t CHANGE COLUMN e e enum('a', 'b', 'c') AUTO_INCREMENT",
				ExpectedErrStr: "Incorrect column specifier for column 'e'",
			},
		},
	},
	{
		// This is with STRICT_TRANS_TABLES or STRICT_ALL_TABLES in sql_mode
		Name:    "enums with zero",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET sql_mode = 'STRICT_TRANS_TABLES';",
			"create table t (e enum('a', 'b', 'c'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "insert into t values (0);",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query:          "insert into t values ('a'), (0), ('b');",
				ExpectedErrStr: "Data truncated for column 'e' at row 2",
			},
			{
				Query:       "create table tt (e enum('a', 'b', 'c') default 0)",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Query: "create table et (e enum('a', 'b', '', 'c'));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:          "insert into et values (0);",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
		},
	},
	{
		Name:    "enums with zero strict all tables",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET sql_mode = 'STRICT_ALL_TABLES';",
			"create table t (e enum('a', 'b', 'c'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "insert into t values (0);",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query:          "insert into t values ('a'), (0), ('b');",
				ExpectedErrStr: "Data truncated for column 'e' at row 2",
			},
			{
				Query:       "create table tt (e enum('a', 'b', 'c') default 0)",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
		},
	},
	{
		Name:    "enums with zero non-strict mode",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET sql_mode = '';",
			"create table t (e enum('a', 'b', 'c'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "insert into t values (0);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t;",
				Expected: []sql.Row{
					{""},
				},
			},
		},
	},
	{
		Name:    "enum import error message validation",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET sql_mode = 'STRICT_TRANS_TABLES';",
			"CREATE TABLE shirts (name VARCHAR(40), size ENUM('x-small', 'small', 'medium', 'large', 'x-large'), color ENUM('red', 'blue'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "INSERT INTO shirts VALUES ('shirt1', 'x-small', 'red');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:          "INSERT INTO shirts VALUES ('shirt2', 'other', 'green');",
				ExpectedErrStr: "Data truncated for column 'size' at row 1",
			},
		},
	},
	{
		Name:    "enum default null validation",
		Dialect: "mysql",
		SetUpScript: []string{
			"SET sql_mode = 'STRICT_TRANS_TABLES';",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "CREATE TABLE test_enum (pk int NOT NULL, e enum('a','b') DEFAULT NULL, PRIMARY KEY (pk));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "INSERT INTO test_enum (pk) VALUES (1);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "SELECT pk, e FROM test_enum;",
				Expected: []sql.Row{
					{1, nil},
				},
			},
		},
	},
	{
		// This is with STRICT_TRANS_TABLES or STRICT_ALL_TABLES in sql_mode
		Skip:    true, // TODO: Fix error type to match MySQL exactly (should be ErrInvalidColumnDefaultValue)
		Name:    "enums with empty string",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (e enum('a', 'b', 'c'));",
			"create table et (e enum('a', 'b', '', 'c'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "insert into t values ('');",
				ExpectedErrStr: "Data truncated for column 'e'", // TODO should be truncated error
			},
			{
				Query:       "create table tt (e enum('a', 'b', 'c') default '')",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Query: "insert into et values (1), (2), (3), (4), ('');",
				Expected: []sql.Row{
					{types.NewOkResult(5)},
				},
			},
			{
				Query: "select e, cast(e as signed) from et order by e;",
				Expected: []sql.Row{
					{"a", 1},
					{"b", 2},
					{"", 3},
					{"", 3},
					{"c", 4},
				},
			},
		},
	},
	{
		Name:    "enum conversions",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (e enum('abc', 'defg', 'hijkl'));",
			"insert into t values(1), (2), (3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select e, length(e) from t order by e;",
				Expected: []sql.Row{
					{"abc", 3},
					{"defg", 4},
					{"hijkl", 5},
				},
			},
			{
				Query: "select e, bit_length(e) from t order by e;",
				Expected: []sql.Row{
					{"abc", 24},
					{"defg", 32},
					{"hijkl", 40},
				},
			},
			{
				Query: "select e, concat(e, 'test') from t order by e;",
				Expected: []sql.Row{
					{"abc", "abctest"},
					{"defg", "defgtest"},
					{"hijkl", "hijkltest"},
				},
			},
			{
				Query: "select e, e like 'a%', e like '%g' from t order by e;",
				Expected: []sql.Row{
					{"abc", true, false},
					{"defg", false, true},
					{"hijkl", false, false},
				},
			},
			{
				Query: "select e from t where e like 'a%' order by e;",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select group_concat(e order by e) as grouped from t;",
				Expected: []sql.Row{
					{"abc,defg,hijkl"},
				},
			},
			{
				Query: "select e from t where e = 'abc';",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select count(*) from t where e = 'defg';",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select (case e when 'abc' then 42 end) from t order by e;",
				Expected: []sql.Row{
					{42},
					{nil},
					{nil},
				},
			},
			{
				Query: "select case when e = 'abc' then 'abc' when e = 'defg' then 123 else e end from t order by e;",
				Expected: []sql.Row{
					{"abc"},
					{"123"},
					{"hijkl"},
				},
			},
			{
				Query: "select (case 'abc' when e then 42 end) from t order by e;",
				Expected: []sql.Row{
					{42},
					{nil},
					{nil},
				},
			},
			{
				Query: "select (case e when 'abc' then e when 'defg' then e when 'hijkl' then e end) as e from t order by e;",
				Expected: []sql.Row{
					{"abc"},
					{"defg"},
					{"hijkl"},
				},
			},
			{
				// https://github.com/dolthub/dolt/issues/8598
				Query: "select (case e when 'abc' then e when 'defg' then e when 'hijkl' then 'something' end) as e from t order by e;",
				Expected: []sql.Row{
					{"abc"},
					{"defg"},
					{"something"},
				},
			},
			{
				// https://github.com/dolthub/dolt/issues/8598
				Query: "select (case e when 'abc' then e when 'defg' then e when 'hijkl' then 123 end) as e from t order by e;",
				Expected: []sql.Row{
					{"123"},
					{"abc"},
					{"defg"},
				},
			},
			{
				Query: "select e, cast(e as signed) from t order by e;",
				Expected: []sql.Row{
					{"abc", 1},
					{"defg", 2},
					{"hijkl", 3},
				},
			},
			{
				Query: "select e, cast(e as char) from t order by e;",
				Expected: []sql.Row{
					{"abc", "abc"},
					{"defg", "defg"},
					{"hijkl", "hijkl"},
				},
			},
			{
				Query: "select e, cast(e as binary) from t order by e;",
				Expected: []sql.Row{
					{"abc", []uint8("abc")},
					{"defg", []uint8("defg")},
					{"hijkl", []uint8("hijkl")},
				},
			},
			{
				Query: "select e from t where e like 'a%'",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select e, cast(e as unsigned) from t order by e;",
				Expected: []sql.Row{
					{"abc", uint64(1)},
					{"defg", uint64(2)},
					{"hijkl", uint64(3)},
				},
			},
			{
				Query: "select e, cast(e as decimal) from t order by e;",
				Expected: []sql.Row{
					{"abc", "1"},
					{"defg", "2"},
					{"hijkl", "3"},
				},
			},
			{
				Query: "select e, cast(e as float) from t order by e;",
				Expected: []sql.Row{
					{"abc", float32(1)},
					{"defg", float32(2)},
					{"hijkl", float32(3)},
				},
			},
			{
				Query: "select e, cast(e as double) from t order by e;",
				Expected: []sql.Row{
					{"abc", float64(1)},
					{"defg", float64(2)},
					{"hijkl", float64(3)},
				},
			},
		},
	},
	{
		Name:    "enum conversion with system variables",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (e enum('ON', 'OFF', 'AUTO'));",
			"set autocommit = 'ON';",
			"insert into t values(@@autocommit), ('OFF'), ('AUTO');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select e, @@autocommit, e = @@autocommit from t order by e;",
				Expected: []sql.Row{
					{"ON", 1, true},
					{"OFF", 1, false},
					{"AUTO", 1, false},
				},
			},
			{
				Query: "select e, concat(e, @@version_comment) from t order by e;",
				Expected: []sql.Row{
					{"ON", "ONDolt"},
					{"OFF", "OFFDolt"},
					{"AUTO", "AUTODolt"},
				},
			},
		},
	},
	{
		Name:    "Convert enum columns to string columns with alter table",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t(pk int primary key, c0 enum('a', 'b', 'c'));",
			"insert into t values(0, 'a'), (1, 'b'), (2, 'c');",
			"alter table t modify column c0 varchar(100);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from t",
				Expected: []sql.Row{{0, "a"}, {1, "b"}, {2, "c"}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9613
		Skip:    true,
		Name:    "Convert enum columns to string columns when copying table",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t(pk int primary key, c0 enum('a', 'b', 'c'));",
			"insert into t values(0, 'a'), (1, 'b'), (2, 'c');",
			"create table tt(pk int primary key, c0 varchar(10))",
			"insert into tt select * from t",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from tt",
				Expected: []sql.Row{{0, "a"}, {1, "b"}, {2, "c"}},
			},
		},
	},
	{
		Name:    "enums with foreign keys",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent (e enum('a', 'b', 'c') primary key);",
			"insert into parent values (1), (2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "create table child0 (e enum('a', 'b', 'c'), foreign key (e) references parent (e));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child0 values (1), (2), (NULL);",
				Expected: []sql.Row{
					{types.NewOkResult(3)},
				},
			},
			{
				Query: "select * from child0 order by e",
				Expected: []sql.Row{
					{nil},
					{"a"},
					{"b"},
				},
			},

			{
				Query: "create table child1 (e enum('x', 'y', 'z'), foreign key (e) references parent (e));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child1 values (1), (2);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child1 values (3);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "insert into child1 values ('x'), ('y');",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child1 values ('z');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:          "insert into child1 values ('a');",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query: "select * from child1 order by e;",
				Expected: []sql.Row{
					{"x"},
					{"x"},
					{"y"},
					{"y"},
				},
			},

			{
				Query: "create table child2 (e enum('b', 'c', 'a'), foreign key (e) references parent (e));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child2 values (1), (2);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child2 values (3);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "insert into child2 values ('c');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:       "insert into child2 values ('a');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "select * from child2 order by e;",
				Expected: []sql.Row{
					{"b"},
					{"c"},
					{"c"},
				},
			},

			{
				Query: "create table child3 (e enum('x', 'y', 'z', 'a', 'b', 'c'), foreign key (e) references parent (e));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child3 values (1), (2);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child3 values (3);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "insert into child3 values ('x'), ('y');",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child3 values ('z');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:       "insert into child3 values ('a');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "select * from child3 order by e;",
				Expected: []sql.Row{
					{"x"},
					{"x"},
					{"y"},
					{"y"},
				},
			},

			{
				Query: "create table child4 (e enum('q'), foreign key (e) references parent (e));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child4 values (1);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:          "insert into child4 values (3);",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query: "insert into child4 values ('q');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:          "insert into child4 values ('a');",
				ExpectedErrStr: "Data truncated for column 'e' at row 1",
			},
			{
				Query: "select * from child4 order by e;",
				Expected: []sql.Row{
					{"q"},
					{"q"},
				},
			},
		},
	},
	{
		Skip:    true,
		Name:    "enums with foreign keys and cascade",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent (e enum('a', 'b', 'c') primary key);",
			"insert into parent values (1), (2);",
			"create table child (e enum('x', 'y', 'z'), foreign key (e) references parent (e) on update cascade on delete cascade);",
			"insert into child values (1), (2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "update parent set e = 'c' where e = 'a';",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1, Info: plan.UpdateInfo{Matched: 1, Updated: 1}}},
				},
			},
			{
				Query: "select * from child order by e;",
				Expected: []sql.Row{
					{"y"},
					{"z"},
				},
			},
			{
				Query: "delete from parent where e = 'b';",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from child order by e;",
				Expected: []sql.Row{
					{"z"},
				},
			},
		},
	},
	{
		Name:    "enums in update and delete statements",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (pk int primary key, e enum('abc', 'def', 'ghi'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "insert into t values (1, 1), (2, 3), (3, 2);",
				Expected: []sql.Row{
					{types.NewOkResult(3)},
				},
			},
			{
				Query: "update t set e = 2 where e = 'ghi';",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						Info: plan.UpdateInfo{
							Matched:  1,
							Updated:  1,
							Warnings: 0,
						},
					}},
				},
			},
			{
				Query: "update t set e = 'ghi' where e = '3';",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 0,
						Info: plan.UpdateInfo{
							Matched:  0,
							Updated:  0,
							Warnings: 0,
						},
					}},
				},
			},
			{
				Query: "select * from t;",
				Expected: []sql.Row{
					{1, "abc"},
					{2, "def"},
					{3, "def"},
				},
			},
			{
				Query: "delete from t where e = 2;",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{1, "abc"},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9024
		Name:    "subqueries should coerce union types to enum",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table enum_table (i int primary key, e enum('a','b') not null)",
			"insert into enum_table values (1,'a'),(2,'b')",
			"create table uv (u int primary key, v varchar(10))",
			"insert into uv values (0, 'bug'),(1,'ant'),(3, null)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from (select e from enum_table union select v from uv) sq",
				Expected: []sql.Row{{"a"}, {"b"}, {"bug"}, {"ant"}, {nil}},
			},
			{
				Query:    "with a as (select e from enum_table union select v from uv) select * from a",
				Expected: []sql.Row{{"a"}, {"b"}, {"bug"}, {"ant"}, {nil}},
			},
		},
	},

	// Set tests
	{
		Name:    "find_in_set tests",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table set_tbl (i int primary key, s set('a','b','c'));",
			"insert into set_tbl values (0, '');",
			"insert into set_tbl values (1, 'a');",
			"insert into set_tbl values (2, 'b');",
			"insert into set_tbl values (3, 'c');",
			"insert into set_tbl values (4, 'a,b');",
			"insert into set_tbl values (6, 'b,c');",
			"insert into set_tbl values (7, 'a,c');",
			"insert into set_tbl values (8, 'a,b,c');",

			"create table collate_tbl (i int primary key, s varchar(10) collate utf8mb4_0900_ai_ci);",
			"insert into collate_tbl values (0, '');",
			"insert into collate_tbl values (1, 'a');",
			"insert into collate_tbl values (2, 'b');",
			"insert into collate_tbl values (3, 'c');",
			"insert into collate_tbl values (4, 'a,b');",
			"insert into collate_tbl values (6, 'b,c');",
			"insert into collate_tbl values (7, 'a,c');",
			"insert into collate_tbl values (8, 'a,b,c');",

			"create table text_tbl (i int primary key, s text);",
			"insert into text_tbl values (0, '');",
			"insert into text_tbl values (1, 'a');",
			"insert into text_tbl values (2, 'b');",
			"insert into text_tbl values (3, 'c');",
			"insert into text_tbl values (4, 'a,b');",
			"insert into text_tbl values (6, 'b,c');",
			"insert into text_tbl values (7, 'a,c');",
			"insert into text_tbl values (8, 'a,b,c');",

			"create table enum_tbl (i int primary key, s enum('a','b','c'));",
			"insert into enum_tbl values (0, 'a'), (1, 'b'), (2, 'c');",
			"select i, s, find_in_set('a', s) from enum_tbl;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select i, find_in_set('a', s) from set_tbl;",
				Expected: []sql.Row{
					{0, 0},
					{1, 1},
					{2, 0},
					{3, 0},
					{4, 1},
					{6, 0},
					{7, 1},
					{8, 1},
				},
			},
			{
				Query: "select i, find_in_set('A', s) from collate_tbl;",
				Expected: []sql.Row{
					{0, 0},
					{1, 1},
					{2, 0},
					{3, 0},
					{4, 1},
					{6, 0},
					{7, 1},
					{8, 1},
				},
			},
			{
				Query: "select i, find_in_set('a', s) from text_tbl;",
				Expected: []sql.Row{
					{0, 0},
					{1, 1},
					{2, 0},
					{3, 0},
					{4, 1},
					{6, 0},
					{7, 1},
					{8, 1},
				},
			},
			{
				Query: "select i, find_in_set('a', s) from enum_tbl;",
				Expected: []sql.Row{
					{0, 1},
					{1, 0},
					{2, 0},
				},
			},
		},
	},
	{
		Name:    "set with empty string",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (i int primary key, s set(''));",
			"insert into t values (0, 0), (1, 1), (2, '');",
			"create table tt (i int primary key, s set('something',''));",
			"insert into tt values (0, 'something,'), (1, ',something,'), (2, ',,,,,,');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select i, s + 0, s from t;",
				Expected: []sql.Row{
					{0, float64(0), ""},
					{1, float64(1), ""},
					{2, float64(0), ""},
				},
			},
			{
				Query: "select i, s + 0, s from t where s = 0;",
				Expected: []sql.Row{
					{0, float64(0), ""},
					{2, float64(0), ""},
				},
			},
			{
				Query: "select i, s + 0, s from t where s = '';",
				Expected: []sql.Row{
					{0, float64(0), ""},
					{1, float64(1), ""}, // We miss this one
					{2, float64(0), ""},
				},
			},
			{
				Query: "select i, s + 0, s from tt;",
				Expected: []sql.Row{
					{0, float64(3), "something,"},
					{1, float64(3), "something,"},
					{2, float64(2), ""},
				},
			},
		},
	},
	{
		Name:    "set conversions",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (s set('abc', 'defg', 'hijkl'));",
			"insert into t values(1), (2), (3), (7);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "select s, length(s) from t order by s;",
				Expected: []sql.Row{
					{"abc", 3},
					{"defg", 4},
					{"abc,defg", 8},
					{"abc,defg,hijkl", 14},
				},
			},
			{
				Query: "select s, bit_length(s) from t order by s;",
				Expected: []sql.Row{
					{"abc", 24},
					{"defg", 32},
					{"abc,defg", 64},
					{"abc,defg,hijkl", 112},
				},
			},
			{
				Query: "select s, concat(s, 'test') from t order by s;",
				Expected: []sql.Row{
					{"abc", "abctest"},
					{"defg", "defgtest"},
					{"abc,defg", "abc,defgtest"},
					{"abc,defg,hijkl", "abc,defg,hijkltest"},
				},
			},
			{
				Query: "select s, s like 'a%', s like '%g' from t order by s;",
				Expected: []sql.Row{
					{"abc", true, false},
					{"defg", false, true},
					{"abc,defg", true, true},
					{"abc,defg,hijkl", true, false},
				},
			},
			{
				Query: "select s from t where s like 'a%' order by s;",
				Expected: []sql.Row{
					{"abc"},
					{"abc,defg"},
					{"abc,defg,hijkl"},
				},
			},
			{
				Query: "select group_concat(s order by s) as grouped from t;",
				Expected: []sql.Row{
					{"abc,defg,abc,defg,abc,defg,hijkl"},
				},
			},
			{
				Query: "select s from t where s = 'abc';",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "select count(*) from t where s = 'defg';",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "select (case s when 'abc' then 42 end) from t order by s;",
				Expected: []sql.Row{
					{42},
					{nil},
					{nil},
					{nil},
				},
			},
			{
				Query: "select case when s = 'abc' then 'abc' when s = 'defg' then 123 else s end from t order by s;",
				Expected: []sql.Row{
					{"abc"},
					{"123"},
					{"abc,defg"},
					{"abc,defg,hijkl"},
				},
			},
			{
				Query: "select (case 'abc' when s then 42 end) from t order by s;",
				Expected: []sql.Row{
					{42},
					{nil},
					{nil},
					{nil},
				},
			},
			{
				Query: "select (case s when 'abc' then s when 'defg' then s when 'hijkl' then s end) as s from t order by s;",
				Expected: []sql.Row{
					{nil},
					{nil},
					{"abc"},
					{"defg"},
				},
			},
			{
				Query: "select (case s when 'abc' then s when 'defg' then s when 'hijkl' then 'something' end) as s from t order by s;",
				Expected: []sql.Row{
					{nil},
					{nil},
					{"abc"},
					{"defg"},
				},
			},
			{
				Query: "select (case s when 'abc' then s when 'defg' then s when 'hijkl' then 123 end) as s from t order by s;",
				Expected: []sql.Row{
					{nil},
					{nil},
					{"abc"},
					{"defg"},
				},
			},
			{
				Query: "select s, cast(s as signed) from t order by s;",
				Expected: []sql.Row{
					{"abc", 1},
					{"defg", 2},
					{"abc,defg", 3},
					{"abc,defg,hijkl", 7},
				},
			},
			{
				Query: "select s, cast(s as char) from t order by s;",
				Expected: []sql.Row{
					{"abc", "abc"},
					{"defg", "defg"},
					{"abc,defg", "abc,defg"},
					{"abc,defg,hijkl", "abc,defg,hijkl"},
				},
			},
			{
				Query: "select s, cast(s as binary) from t order by s;",
				Expected: []sql.Row{
					{"abc", []uint8("abc")},
					{"defg", []uint8("defg")},
					{"abc,defg", []uint8("abc,defg")},
					{"abc,defg,hijkl", []uint8("abc,defg,hijkl")},
				},
			},
			{
				Query: "select s, cast(s as unsigned) from t order by s;",
				Expected: []sql.Row{
					{"abc", uint64(1)},
					{"defg", uint64(2)},
					{"abc,defg", uint64(3)},
					{"abc,defg,hijkl", uint64(7)},
				},
			},
			{
				Query: "select s, cast(s as decimal) from t order by s;",
				Expected: []sql.Row{
					{"abc", "1"},
					{"defg", "2"},
					{"abc,defg", "3"},
					{"abc,defg,hijkl", "7"},
				},
			},
			{
				Query: "select s, cast(s as float) from t order by s;",
				Expected: []sql.Row{
					{"abc", float32(1)},
					{"defg", float32(2)},
					{"abc,defg", float32(3)},
					{"abc,defg,hijkl", float32(7)},
				},
			},
			{
				Query: "select s, cast(s as double) from t order by s;",
				Expected: []sql.Row{
					{"abc", float64(1)},
					{"defg", float64(2)},
					{"abc,defg", float64(3)},
					{"abc,defg,hijkl", float64(7)},
				},
			},
		},
	},
	{
		Name:    "Convert set columns to string columns with alter table",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t(pk int primary key, c0 set('abc', 'def','ghi'))",
			"insert into t values(0, 'abc,def'), (1, 'def'), (2, 'ghi');",
			"alter table t modify column c0 varchar(100);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from t",
				Expected: []sql.Row{{0, "abc,def"}, {1, "def"}, {2, "ghi"}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9613
		Skip:    true,
		Name:    "Convert set columns to string columns when copying table",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t(pk int primary key, c0 set('abc', 'def','ghi'))",
			"insert into t values(0, 'abc,def'), (1, 'def'), (2, 'ghi');",
			"create table tt(pk int primary key, c0 varchar(10))",
			"insert into tt select * from t",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from tt",
				Expected: []sql.Row{{0, "abc,def"}, {1, "def"}, {2, "ghi"}},
			},
		},
	},
	{
		Name:    "set with duplicates",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (s set('a', 'b', 'c'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "insert into t values ('a,b,a,c,a,b,b,b,c,c,c,a,a');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select s + 0, s from t;",
				Expected: []sql.Row{
					{float64(7), "a,b,c"},
				},
			},
			{
				// This is with STRICT_TRANS_TABLES; errors are warnings when not strict
				Query:       "create table tt (s set('a', 'a'));",
				ExpectedErr: sql.ErrDuplicateEntrySet,
			},
		},
	},
	{
		Name:    "set in update and delete statements",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (pk int primary key, s set('abc', 'def'));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "insert into t values (0, 0), (1, 1), (2, 3), (3, 2);",
				Expected: []sql.Row{
					{types.NewOkResult(4)},
				},
			},
			{
				Query: "update t set s = 3 where s = 2;",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						Info: plan.UpdateInfo{
							Matched:  1,
							Updated:  1,
							Warnings: 0,
						},
					}},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{0, ""},
					{1, "abc"},
					{2, "abc,def"},
					{3, "abc,def"},
				},
			},
			{
				Query: "delete from t where s = 'abc,def'",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query: "select * from t",
				Expected: []sql.Row{
					{0, ""},
					{1, "abc"},
				},
			},
		},
	},
	{
		Name:    "set with auto increment",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t (s set('a', 'b', 'c') primary key);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table t2 (s set('a', 'b', 'c') primary key auto_increment)",
				ExpectedErrStr: "Incorrect column specifier for column 's'",
			},
			{
				Query:          "alter table t modify s set('a', 'b', 'c') auto_increment;",
				ExpectedErrStr: "Incorrect column specifier for column 's'",
			},
			{
				Query:          "alter table t modify column s set('a', 'b', 'c') auto_increment;",
				ExpectedErrStr: "Incorrect column specifier for column 's'",
			},
			{
				Query:          "alter table t change s s set('a', 'b', 'c') auto_increment;",
				ExpectedErrStr: "Incorrect column specifier for column 's'",
			},
			{
				Query:          "alter table t change column s s set('a', 'b', 'c') auto_increment;",
				ExpectedErrStr: "Incorrect column specifier for column 's'",
			},
		},
	},
	{
		Name:        "set with default values",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Skip:        true,
				Query:       "create table bad (s set('a', 'b', 'c') default 0);",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Skip:        true,
				Query:       "create table bad (s set('a', 'b', 'c') default 1);",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Skip:        true,
				Query:       "create table bad (s set('a', 'b', 'c') default 'notexists');",
				ExpectedErr: sql.ErrInvalidColumnDefaultValue,
			},
			{
				Query: "create table t0 (s set('a', 'b', 'c') default (0));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into t0 values ();",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t0",
				Expected: []sql.Row{
					{""},
				},
			},

			{
				Query: "create table t (s set('a', 'b', 'c') not null);",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Skip:        true,
				Query:       "insert into t values ();",
				ExpectedErr: sql.ErrInsertIntoNonNullableDefaultNullColumn, // wrong error
			},
			{
				Skip:        true,
				Query:       "insert into t values (default);",
				ExpectedErr: sql.ErrInsertIntoNonNullableDefaultNullColumn, // wrong error
			},
		},
	},
	{
		Name:    "set with collations",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t1 (s set('a', 'b', 'c') collate utf8mb4_0900_ai_ci);",
			"create table t2 (s set('a', 'b', 'c') collate utf8mb4_0900_bin);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table t1;",
				Expected: []sql.Row{
					{"t1", "CREATE TABLE `t1` (\n" +
						"  `s` set('a','b','c') COLLATE utf8mb4_0900_ai_ci\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "insert into t1 values ('A,B,c');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from t1",
				Expected: []sql.Row{
					{"a,b,c"},
				},
			},
			{
				Query: "show create table t2;",
				Expected: []sql.Row{
					{"t2", "CREATE TABLE `t2` (\n" +
						"  `s` set('a','b','c')\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query:          "insert into t2 values ('A,B,c');",
				ExpectedErrStr: "Data truncated for column 's' at row 1",
			},
			{
				Query:    "select * from t2",
				Expected: []sql.Row{},
			},
			{
				Query:       "create table bad (s set('a', 'A') collate utf8mb4_0900_ai_ci);",
				ExpectedErr: sql.ErrDuplicateEntrySet,
			},
		},
	},
	{
		Name:    "set with foreign keys",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent (s set('a', 'b', 'c') primary key);",
			"insert into parent values (1), (2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "create table child0 (s set('a', 'b', 'c'), foreign key (s) references parent (s));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child0 values (1), (2), (NULL);",
				Expected: []sql.Row{
					{types.NewOkResult(3)},
				},
			},
			{
				Query: "select * from child0 order by s;",
				Expected: []sql.Row{
					{nil},
					{"a"},
					{"b"},
				},
			},

			{
				Query: "create table child1 (s set('x', 'y', 'z'), foreign key (s) references parent (s));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child1 values (1), (2);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child1 values (3);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "insert into child1 values ('x'), ('y');",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child1 values ('z');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:          "insert into child1 values ('a');",
				ExpectedErrStr: "Data truncated for column 's' at row 1",
			},
			{
				Query: "select * from child1 order by s;",
				Expected: []sql.Row{
					{"x"},
					{"x"},
					{"y"},
					{"y"},
				},
			},

			{
				Query: "create table child2 (s set('b', 'c', 'a'), foreign key (s) references parent (s));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child2 values (1), (2);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child2 values (3);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "insert into child2 values ('c');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:       "insert into child2 values ('a');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "select * from child2 order by s;",
				Expected: []sql.Row{
					{"b"},
					{"c"},
					{"c"},
				},
			},

			{
				Query: "create table child3 (s set('x', 'y', 'z', 'a', 'b', 'c'), foreign key (s) references parent (s));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child3 values (1), (2);",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child3 values (3);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "insert into child3 values ('x'), ('y');",
				Expected: []sql.Row{
					{types.NewOkResult(2)},
				},
			},
			{
				Query:       "insert into child3 values ('z');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:       "insert into child3 values ('a');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "select * from child3 order by s;",
				Expected: []sql.Row{
					{"x"},
					{"x"},
					{"y"},
					{"y"},
				},
			},

			{
				Query: "create table child4 (s set('q'), foreign key (s) references parent (s));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child4 values (1);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:          "insert into child4 values (3);",
				ExpectedErrStr: "Data truncated for column 's' at row 1",
			},
			{
				Query: "insert into child4 values ('q');",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query:          "insert into child4 values ('a');",
				ExpectedErrStr: "Data truncated for column 's' at row 1",
			},
			{
				Query: "select * from child4 order by s;",
				Expected: []sql.Row{
					{"q"},
					{"q"},
				},
			},
		},
	},
	{
		Name:    "set with foreign keys and cascade",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent (s set('a', 'b', 'c') primary key);",
			"insert into parent values (1), (2);",
			"create table child (s set('x', 'y', 'z'), foreign key (s) references parent (s) on update cascade on delete cascade);",
			"insert into child values (1), (2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "update parent set s = 'c' where s = 'a';",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1, Info: plan.UpdateInfo{Matched: 1, Updated: 1}}},
				},
			},
			{
				Query: "select * from child order by s;",
				Expected: []sql.Row{
					{"y"},
					{"z"},
				},
			},
			{
				Query: "delete from parent where s = 'b';",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "select * from child order by s;",
				Expected: []sql.Row{
					{"z"},
				},
			},
		},
	},

	// Bit Tests
	{
		Name:        "bit with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (b bit(1) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'b'",
			},
			{
				Query:          "create table bad (b bit(64) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'b'",
			},
		},
	},

	// Bool Tests
	{
		Name:    "bool with auto_increment",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table bool_tbl (b bool primary key auto_increment);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table bool_tbl;",
				Expected: []sql.Row{
					{"bool_tbl", "CREATE TABLE `bool_tbl` (\n" +
						"  `b` tinyint(1) NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`b`)\n" +
						") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},

	// Int Tests
	{
		// https://github.com/dolthub/dolt/issues/9530
		Name:    "int with auto_increment",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table tinyint_tbl (i tinyint primary key auto_increment);",
			"create table smallint_tbl (i smallint primary key auto_increment);",
			"create table mediumint_tbl (i mediumint primary key auto_increment);",
			"create table int_tbl (i int primary key auto_increment);",
			"create table bigint_tbl (i bigint primary key auto_increment);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "insert into tinyint_tbl values (999)",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into tinyint_tbl values (127)",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     127,
					}},
				},
			},
			{
				Query: "show create table tinyint_tbl;",
				Expected: []sql.Row{
					{"tinyint_tbl", "CREATE TABLE `tinyint_tbl` (\n" +
						"  `i` tinyint NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into smallint_tbl values (99999);",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into smallint_tbl values (32767);",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     32767,
					}},
				},
			},
			{
				Query: "show create table smallint_tbl;",
				Expected: []sql.Row{
					{"smallint_tbl", "CREATE TABLE `smallint_tbl` (\n" +
						"  `i` smallint NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=32767 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into mediumint_tbl values (99999999);",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into mediumint_tbl values (8388607);",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     8388607,
					}},
				},
			},
			{
				Query: "show create table mediumint_tbl;",
				Expected: []sql.Row{
					{"mediumint_tbl", "CREATE TABLE `mediumint_tbl` (\n" +
						"  `i` mediumint NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=8388607 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into int_tbl values (99999999999)",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into int_tbl values (2147483647)",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     2147483647,
					}},
				},
			},
			{
				Query: "show create table int_tbl;",
				Expected: []sql.Row{
					{"int_tbl", "CREATE TABLE `int_tbl` (\n" +
						"  `i` int NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=2147483647 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into bigint_tbl values (99999999999999999999);",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into bigint_tbl values (9223372036854775807);",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     9223372036854775807,
					}},
				},
			},
			{
				Query: "show create table bigint_tbl;",
				Expected: []sql.Row{
					{"bigint_tbl", "CREATE TABLE `bigint_tbl` (\n" +
						"  `i` bigint NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=9223372036854775807 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9530
		Name:    "unsigned int with auto_increment",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table tinyint_tbl (i tinyint unsigned primary key auto_increment);",
			"create table smallint_tbl (i smallint unsigned primary key auto_increment);",
			"create table mediumint_tbl (i mediumint unsigned primary key auto_increment);",
			"create table int_tbl (i int unsigned primary key auto_increment);",
			"create table bigint_tbl (i bigint unsigned primary key auto_increment);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "insert into tinyint_tbl values (999)",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into tinyint_tbl values (255)",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     255,
					}},
				},
			},
			{
				Query: "show create table tinyint_tbl;",
				Expected: []sql.Row{
					{"tinyint_tbl", "CREATE TABLE `tinyint_tbl` (\n" +
						"  `i` tinyint unsigned NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=255 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into smallint_tbl values (99999);",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into smallint_tbl values (65535);",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     65535,
					}},
				},
			},
			{
				Query: "show create table smallint_tbl;",
				Expected: []sql.Row{
					{"smallint_tbl", "CREATE TABLE `smallint_tbl` (\n" +
						"  `i` smallint unsigned NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=65535 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into mediumint_tbl values (999999999);",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into mediumint_tbl values (16777215);",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     16777215,
					}},
				},
			},
			{
				Query: "show create table mediumint_tbl;",
				Expected: []sql.Row{
					{"mediumint_tbl", "CREATE TABLE `mediumint_tbl` (\n" +
						"  `i` mediumint unsigned NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=16777215 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into int_tbl values (99999999999)",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into int_tbl values (4294967295)",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     4294967295,
					}},
				},
			},
			{
				Query: "show create table int_tbl;",
				Expected: []sql.Row{
					{"int_tbl", "CREATE TABLE `int_tbl` (\n" +
						"  `i` int unsigned NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=4294967295 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},

			{
				Query:       "insert into bigint_tbl values (999999999999999999999);",
				ExpectedErr: sql.ErrValueOutOfRange,
			},
			{
				Query: "insert into bigint_tbl values (18446744073709551615);",
				Expected: []sql.Row{
					{types.OkResult{
						RowsAffected: 1,
						InsertID:     18446744073709551615,
					}},
				},
			},
			{
				Query: "show create table bigint_tbl;",
				Expected: []sql.Row{
					{"bigint_tbl", "CREATE TABLE `bigint_tbl` (\n" +
						"  `i` bigint unsigned NOT NULL AUTO_INCREMENT,\n" +
						"  PRIMARY KEY (`i`)\n" +
						") ENGINE=InnoDB AUTO_INCREMENT=18446744073709551615 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
		},
	},

	// Float Tests
	{
		Name:        "float with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table float_tbl (f float primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'f'",
			},
		},
	},

	// Double Tests
	{
		Name:        "double with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table double_tbl (d double primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'd'",
			},
		},
	},

	// Decimal Tests
	{
		Name:        "decimal with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (d decimal primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'd'",
			},
			{
				Query:          "create table bad (d decimal(65,30) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'd'",
			},
		},
	},
	{
		Name:    "decimals with foreign keys",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent (d decimal(4, 2) primary key);",
			"insert into parent values (1.23), (45.67), (78.9);",
			"create table parent_multi (d1 decimal(4,2), d2 decimal(3,1), primary key (d1, d2));",
			"insert into parent_multi values (1.23, 4.5), (45.67, 78.9), (99.99, 0.1);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "create table child_dec_4_2 (d decimal(4,2), foreign key (d) references parent (d));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child_dec_4_2 values (1.23), (45.67), (NULL);",
				Expected: []sql.Row{
					{types.NewOkResult(3)},
				},
			},
			{
				Query: "insert into child_dec_4_2 values (1.229999), (45.6711111), (78.90);",
				Expected: []sql.Row{
					{types.NewOkResult(3)},
				},
			},
			{
				Query:       "insert into child_dec_4_2 values (99.99);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child_dec_4_1 (d decimal(4,1), foreign key (d) references parent (d));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_dec_4_1 values (78.9);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:       "insert into child_dec_4_1 values (99.9);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child_dec_3_2 (d decimal(3,2), foreign key (d) references parent (d));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "insert into child_dec_3_2 values (1.23);",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				Query: "create table child_dec_65_30 (d decimal(65,30), foreign key (d) references parent (d));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_dec_65_30 values (1.23);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child_multi_4_2_3_1 (d1 decimal(4,2), d2 decimal(3,1), foreign key (d1, d2) references parent_multi (d1, d2));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				}},
			{
				Query: "insert into child_multi_4_2_3_1 values (1.23, 4.5), (45.67, 78.9), (NULL, NULL);",
				Expected: []sql.Row{
					{types.NewOkResult(3)},
				},
			},
			{
				Query:       "insert into child_multi_4_2_3_1 values (1.23, 9.9);",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
		},
	},
	{
		Name: "decimal unique key",
		SetUpScript: []string{
			"create table t (i int primary key, d decimal(10, 2) unique)",
			"insert into t values (1, 1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "insert into t values (2, 1)",
				ExpectedErr: sql.ErrUniqueKeyViolation,
			},
		},
	},

	// Date Tests
	{
		Name:        "date with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (d date primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'd'",
			},
		},
	},

	// Datetime Tests
	{
		Name:        "datetime with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (dt datetime primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'dt'",
			},
			{
				Query:          "create table bad (dt datetime(6) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'dt'",
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9544
		Name:    "datetime with foreign keys",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent_datetime0 (dt datetime primary key);",
			"insert into parent_datetime0 values ('2001-02-03 12:34:56');",
			"create table parent_datetime6 (dt datetime(6) primary key);",
			"insert into parent_datetime6 values ('2001-02-03 12:34:56');",
			"insert into parent_datetime6 values ('2001-02-03 12:34:56.123456');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "create table child_datetime0 (dt datetime, foreign key (dt) references parent_datetime6(dt));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_datetime0 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child_datetime6 (dt datetime(6), foreign key (dt) references parent_datetime0(dt));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_datetime6 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},

			{
				Query: "create table child1_timestamp0 (ts timestamp, foreign key (ts) references parent_datetime0(dt));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child1_timestamp0 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child2_timestamp0 (ts timestamp, foreign key (ts) references parent_datetime6(dt));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child2_timestamp0 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},

			{
				Query: "create table child1_timestamp6 (ts timestamp(6), foreign key (ts) references parent_datetime0(dt));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child1_timestamp6 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child2_timestamp6 (ts timestamp(6), foreign key (ts) references parent_datetime6(dt));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child2_timestamp6 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:       "insert into child2_timestamp6 values ('2001-02-03 12:34:56.123456');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
		},
	},

	// Timestamp Tests
	{
		Name:        "timestamp with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (ts timestamp primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'ts'",
			},
			{
				Query:          "create table bad (ts timestamp(6) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'ts'",
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9544
		Name:    "timestamps with foreign keys",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent_timestamp0 (ts timestamp primary key);",
			"insert into parent_timestamp0 values ('2001-02-03 12:34:56');",
			"create table parent_timestamp6 (ts timestamp(6) primary key);",
			"insert into parent_timestamp6 values ('2001-02-03 12:34:56');",
			"insert into parent_timestamp6 values ('2001-02-03 12:34:56.123456');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "create table child_timestamp0 (ts timestamp, foreign key (ts) references parent_timestamp6(ts));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_timestamp0 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child_timestamp6 (ts timestamp(6), foreign key (ts) references parent_timestamp0(ts));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_timestamp6 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},

			{
				Query: "create table child1_datetime0 (dt datetime, foreign key (dt) references parent_timestamp0(ts));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child1_datetime0 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child2_datetime0 (dt datetime, foreign key (dt) references parent_timestamp6(ts));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child2_datetime0 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},

			{
				Query: "create table child1_datetime6 (dt datetime(6), foreign key (dt) references parent_timestamp0(ts));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child1_datetime6 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query: "create table child2_datetime6 (dt datetime(6), foreign key (dt) references parent_timestamp6(ts));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child2_datetime6 values ('2001-02-03 12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
			{
				Query:       "insert into child2_datetime6 values ('2001-02-03 12:34:56.123456');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
			},
		},
	},

	// Time Tests
	{
		Name:        "time with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (t time primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 't'",
			},
			{
				Query:          "create table bad (t time(6) primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 't'",
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9544
		Name:    "time with foreign keys",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table parent_time0 (t time primary key);",
			"insert into parent_time0 values ('12:34:56');",
			"create table parent_time6 (t time(6) primary key);",
			"insert into parent_time6 values ('12:34:56');",
			"insert into parent_time6 values ('12:34:56.123456');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "create table child_time0 (t time, foreign key (t) references parent_time6(t));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_time0 values ('12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
				Skip:        true, // TODO: Fix TIME precision handling in foreign key constraints (https://github.com/dolthub/dolt/issues/9544)
			},
			{
				Query: "create table child_time6 (t time(6), foreign key (t) references parent_time0(t));",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query:       "insert into child_time6 values ('12:34:56');",
				ExpectedErr: sql.ErrForeignKeyChildViolation,
				Skip:        true, // TODO: Fix TIME precision handling in foreign key constraints (https://github.com/dolthub/dolt/issues/9544)
			},
		},
	},

	// Year Tests
	{
		Name:        "year with auto_increment",
		Dialect:     "mysql",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "create table bad (y year primary key auto_increment);",
				ExpectedErrStr: "Incorrect column specifier for column 'y'",
			},
		},
	},

	{
		// TODO: This test currently fails in Doltgres because Doltgres does not allow `create table...as select...`
		//   even though it's a valid Postgres query. Remove Dialect tag once fixed in Doltgres
		//   https://github.com/dolthub/doltgresql/issues/1669
		Dialect: "mysql",
		Name:    "union field indexes",
		SetUpScript: []string{
			"create table t(id int primary key auto_increment, words varchar(100))",
			"insert into t(words) values ('foo'),('bar'),('baz'),('zap')",
			"create table t2 as select * from t",
			"update t2 set words = 'boo' where id = 1",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `
select * from (
    select id, words from t
    union
    select id, words from t2
) as combined
where
    combined.id = 1;
`,
				Expected: []sql.Row{
					{1, "foo"},
					{1, "boo"},
				},
			},
			{
				Query: `
select * from (
    select 'parent' as tbl, id, words from t
    union
    select 'child' as tbl, id, words from t2
) as combined
where
    combined.id = 1;
`,
				Expected: []sql.Row{
					{"parent", 1, "foo"},
					{"child", 1, "boo"},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9628
		Name: "UNION column mapping bug dolt#9628",
		SetUpScript: []string{
			"CREATE TABLE report_card (id INT PRIMARY KEY, name VARCHAR(255), entity_id INT, dashboard_id INT, description TEXT, display TEXT, collection_preview TEXT, dataset_query TEXT, collection_id INT, archived_directly BOOLEAN DEFAULT FALSE, collection_position INT, database_id INT, archived BOOLEAN DEFAULT FALSE, last_used_at DATETIME, table_id INT, query_type VARCHAR(50), type VARCHAR(50))",
			"CREATE TABLE collection (id INT PRIMARY KEY, name VARCHAR(255), entity_id INT, location VARCHAR(500), authority_level VARCHAR(50), personal_owner_id INT, archived_directly BOOLEAN DEFAULT FALSE, type VARCHAR(50), archived BOOLEAN DEFAULT FALSE, namespace VARCHAR(100))",
			"CREATE TABLE report_dashboard (id INT PRIMARY KEY, name VARCHAR(255), entity_id INT, description TEXT, collection_id INT, archived_directly BOOLEAN DEFAULT FALSE, collection_position INT, archived BOOLEAN DEFAULT FALSE, last_viewed_at DATETIME)",
			"INSERT INTO report_card (id, name, entity_id, dashboard_id, type, archived_directly, archived, collection_position) VALUES (2, 'Card 2', 1002, NULL, 'question', FALSE, FALSE, NULL), (3, 'Card 3', 1003, NULL, 'question', FALSE, FALSE, NULL)",
			"INSERT INTO collection (id, name, entity_id, location, type, archived, personal_owner_id, namespace) VALUES (2, 'Collection 2', 4002, '/test2/', 'normal', FALSE, NULL, NULL), (3, 'Collection 3', 4003, '/test3/', 'normal', FALSE, NULL, NULL)",
			"INSERT INTO report_dashboard (id, name, entity_id, description, collection_id, archived_directly, archived, collection_position) VALUES (1, 'Dashboard 1', 3001, 'Test dashboard description', NULL, FALSE, FALSE, NULL), (2, 'Dashboard 2', 3002, 'Test dashboard 2 description', NULL, FALSE, FALSE, NULL)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `WITH visible_collection_ids AS (SELECT id FROM collection AS c WHERE (1 <> c.id)) 
				SELECT 5 AS model_ranking, c.id, c.name, c.description, c.entity_id, c.display, c.collection_preview, c.dataset_query, c.collection_id, 'card' AS model 
				FROM report_card AS c WHERE (c.dashboard_id IS NULL) AND (archived = FALSE) AND (c.type = 'question') 
				UNION 
				SELECT 7 AS model_ranking, id, name, name AS description, entity_id, NULL AS display, NULL AS collection_preview, NULL AS dataset_query, id AS collection_id, 'collection' AS model 
				FROM collection AS col WHERE (archived = FALSE) AND (id <> 1) AND (personal_owner_id IS NULL) AND (namespace IS NULL) 
				UNION 
				SELECT 1 AS model_ranking, d.id, d.name, d.description, d.entity_id, NULL AS display, NULL AS collection_preview, NULL AS dataset_query, NULL AS collection_id, 'dashboard' AS model 
				FROM report_dashboard AS d WHERE (archived = FALSE)`,
				Expected: []sql.Row{
					{5, 2, "Card 2", nil, 1002, nil, nil, nil, nil, "card"},
					{5, 3, "Card 3", nil, 1003, nil, nil, nil, nil, "card"},
					{7, 2, "Collection 2", "Collection 2", 4002, nil, nil, nil, 2, "collection"},
					{7, 3, "Collection 3", "Collection 3", 4003, nil, nil, nil, 3, "collection"},
					{1, 1, "Dashboard 1", "Test dashboard description", 3001, nil, nil, nil, nil, "dashboard"},
					{1, 2, "Dashboard 2", "Test dashboard 2 description", 3002, nil, nil, nil, nil, "dashboard"},
				},
			},
		},
	},
	{
		// TODO: Doltgres does not support this query
		// https://github.com/dolthub/dolt/issues/9631
		Name:    "test union/intersect/except over subqueries over joins",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table t1 (i int primary key);",
			"create table t2 (j int primary key);",
			"insert into t1 values (0), (1);",
			"insert into t2 values (0), (2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `
select * from t1 union (
    select j from t2
    where (
        j > 10
        or
        j in (
            select j from t1 join t2
        )
    )
);
`,
				Expected: []sql.Row{
					{0},
					{1},
					{2},
				},
			},
			{
				Query: `
select * from t1 intersect (
    select j from t2
    where (
        j > 10
        or
        j in (
            select j from t1 join t2
        )
    )
);
`,
				Expected: []sql.Row{
					{0},
				},
			},
			{
				Query: `
select * from t1 except (
    select j from t2
    where (
        j > 10
        or
        j in (
            select j from t1 join t2
        )
    )
);
`,
				Expected: []sql.Row{
					{1},
				},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/6899
		Name: "window function tests",
		SetUpScript: []string{
			"CREATE TABLE c (c_id INT PRIMARY KEY, bill TEXT);",
			"CREATE TABLE o (o_id INT PRIMARY KEY, c_id INT, ship TEXT);",
			"INSERT INTO c VALUES (1, 'CA'), (2, 'TX'), (3, 'MA'), (4, 'TX'), (5, NULL), (6, 'FL');",
			"INSERT INTO o VALUES (10, 1, 'CA'), (20, 1, 'CA'), (30, 1, 'CA'), (40, 2, 'CA'), (50, 2, 'TX'), (60, 2, NULL), (70, 4, 'WY'), (80, 4, NULL), (90, 6, 'WA');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select row_number() over () as rn from o where c_id=-999",
				Expected: []sql.Row{},
			},
			{
				// TODO: valid query in Postgres. https://github.com/dolthub/doltgresql/issues/1796
				Dialect:  "mysql",
				Query:    "select row_number() over () as rn from o where c_id=1",
				Expected: []sql.Row{{1}, {2}, {3}},
			},
			{
				Query:    "select rank() over() as rnk from o where c_id=-999",
				Expected: []sql.Row{},
			},
			{
				// TODO: valid query in Postgres. https://github.com/dolthub/doltgresql/issues/1796
				Dialect: "mysql",
				Query:   "select o_id, c_id, rank() over(order by o_id) as rnk from o where c_id=1",
				Expected: []sql.Row{
					{10, 1, uint64(1)},
					{20, 1, uint64(2)},
					{30, 1, uint64(3)},
				},
			},
			{
				Query:    "select dense_rank() over() as rnk from o where c_id=-999",
				Expected: []sql.Row{},
			},
			{
				// TODO: valid query in Postgres. But Postgres orders nil at the end. Maybe rewrite query to filter out
				//  ship=null https://github.com/dolthub/doltgresql/issues/1796
				Dialect: "mysql",
				Query:   "select ship, dense_rank() over (order by ship) as drnk from o where c_id in (1, 2) order by ship",
				Expected: []sql.Row{
					{nil, uint64(1)},
					{"CA", uint64(2)},
					{"CA", uint64(2)},
					{"CA", uint64(2)},
					{"CA", uint64(2)},
					{"TX", uint64(3)},
				},
			},
			{
				Query:    "select count(*) from o where c_id=-999",
				Expected: []sql.Row{{0}},
			},
		},
	},
	{
		Name:    "aggregate function with match",
		Dialect: "mysql",
		SetUpScript: []string{
			"CREATE TABLE test (pk INT PRIMARY KEY, doc TEXT, FULLTEXT idx (doc));",
			"INSERT INTO test VALUES (2, 'g hhhh aaaab ooooo aaaa'), (1, 'bbbb ff cccc ddd eee'), (4, 'AAAA aaaa aaaac aaaa Aaaa aaaa'), (3, 'aaaA ff j kkkk llllllll');",
		},
		Assertions: []ScriptTestAssertion{
			{
				// https://github.com/dolthub/dolt/issues/9761
				Skip:        true,
				Query:       "SELECT pk, count(pk), MATCH(doc) AGAINST('aaaa') AS relevancy FROM test ORDER BY relevancy DESC;",
				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
			},
			{
				Query:    "SET SESSION sql_mode = REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '');",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},

			{
				Query:    "SELECT pk, count(pk), MATCH(doc) AGAINST('aaaa') AS relevancy FROM test ORDER BY relevancy DESC;",
				Expected: []sql.Row{{1, 4, float64(0)}},
			},
		},
	},
	{
		Name:    "nonaggregated column in aggregated query",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table emptytable(i mediumint primary key, s varchar(20))",
			"create table mytable(i bigint primary key, s varchar(20))",
			"insert into mytable values (1, 'first'), (2, 'second'), (3, 'third');",
			"create table two_pk (pk1 tinyint, pk2 tinyint, c1 tinyint NOT NULL, primary key (pk1, pk2))",
			"insert into two_pk values (0,0,0), (0,1,10), (1,0,20), (1,1,30)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM emptytable;",
				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
			},
			{
				Query:       "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM mytable where false;",
				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
			},
			{
				Query:       "SELECT pk1, SUM(c1) FROM two_pk",
				ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
			},
			{
				Query:    "SET SESSION sql_mode = REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '');",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM emptytable;",
				Expected: []sql.Row{
					{0, nil, nil, 123, "abc", "abcdef"},
				},
			},
			{
				Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM mytable where false;",
				Expected: []sql.Row{
					{0, nil, nil, 123, "abc", "abcdef"},
				},
			},
			{
				Query:    "SELECT pk1, SUM(c1) FROM two_pk",
				Expected: []sql.Row{{0, 60.0}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9789
		Name: "order by on empty set from joins",
		SetUpScript: []string{
			"create table t0(c0 int, primary key(c0))",
			"create table t1(c0 int)",
			"create table t2(c0 int)",
			"create table t3(c0 int)",
			"insert into t0 values (1)",
			"insert into t1 values (1)",
			"insert into t2 values (1)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from t2, t3, t0, t1",
				Expected: []sql.Row{},
			},
			{
				Query:    "select * from t2, t3, t0, t1 order by t0.c0",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name:    "pipes as concat mode",
		Dialect: "mysql",
		SetUpScript: []string{
			"create table names(first_name varchar(20), last_name varchar(20))",
			"insert into names values ('john', 'smith'), ('bob','burger')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select true || false",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select '0' || '0'",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "select 'Hello' || ' ' || 'World'",
				Expected: []sql.Row{{false}},
			},
			{
				Query:    "select 1 || 0",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select first_name || ' ' || last_name as full_name from names order by full_name",
				Expected: []sql.Row{{false}, {false}},
			},
			{
				Query:    "select 1 + 2 || 3 + 4",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select true || 1 || 'abc'",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select (1 || 2) || (3 || 4)",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select (1 + 2) || (3 + 4)",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select ((1 || 2) || 3) || 4",
				Expected: []sql.Row{{true}},
			},
			{
				Query:    "select ((1 + 2) || 3) + 4",
				Expected: []sql.Row{{5}},
			},
			{
				Query:    "SET SESSION sql_mode = CONCAT(@@SESSION.sql_mode, ',PIPES_AS_CONCAT');",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "select true || false",
				Expected: []sql.Row{{"10"}},
			},
			{
				Query:    "select '0' || '0'",
				Expected: []sql.Row{{"00"}},
			},
			{
				Query:    "select 'Hello' || ' ' || 'World'",
				Expected: []sql.Row{{"Hello World"}},
			},
			{
				Query:    "select 1 || 0",
				Expected: []sql.Row{{"10"}},
			},
			{
				Query:    "select first_name || ' ' || last_name as full_name from names order by full_name",
				Expected: []sql.Row{{"bob burger"}, {"john smith"}},
			},
			{
				Query:    "select 1 + 2 || 3 + 4",
				Expected: []sql.Row{{float64(28)}},
			},
			{
				Query:    "select true || 1 || 'abc'",
				Expected: []sql.Row{{"11abc"}},
			},
			{
				Query:    "select (1 || 2) || (3 || 4)",
				Expected: []sql.Row{{"1234"}},
			},
			{
				Query:    "select (1 + 2) || (3 + 4)",
				Expected: []sql.Row{{"37"}},
			},
			{
				Query:    "select ((1 || 2) || 3) || 4",
				Expected: []sql.Row{{"1234"}},
			},
			{
				Query:    "select ((1 + 2) || 3) + 4",
				Expected: []sql.Row{{float64(37)}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/9951
		Name: "Do not prune tables that are part of a semi-join",
		SetUpScript: []string{
			"create table t0(id int primary key, name longtext, description longtext, comment longtext, created_at timestamp(6), archived bool)",
			"insert into t0 values (1, 'first', 'abc', 'def', '2025-10-14 10:00:00', b'0')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from (select id, name, description, archived from t0 as test0 where (id in (select id from t0))) as dummy_alias order by dummy_alias.name asc;",
				Expected: []sql.Row{{1, "first", "abc", 0}},
			},
		},
	},
	{
		// https://github.com/dolthub/dolt/issues/10064
		Name: "Hoist out of scope filters for left and right sides of union",
		SetUpScript: []string{
			"create table t1(c0 int, c1 varchar(500))",
			"insert into t1(c1, c0) values ('-1',1),('-2',2)",
		},
		Assertions: []ScriptTestAssertion{
			{
				// Ensures that out of scope filters are hoisted
				Query: "SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM (SELECT NULL WHERE FALSE) AS sub0 WHERE (t1.c0)*(t1.c0)) union all SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM (SELECT NULL WHERE FALSE) AS sub0 WHERE (t1.c0)*(t1.c0));",
				Expected: []sql.Row{
					{1, "-1"},
					{2, "-2"},
					{1, "-1"},
					{2, "-2"},
				},
			},
			{
				// Ensures that antijoin iterator works correctly
				Query: "SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM (SELECT NULL WHERE FALSE) AS sub0) union all SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM (SELECT NULL WHERE FALSE) AS sub0);",
				Expected: []sql.Row{
					{1, "-1"},
					{2, "-2"},
					{1, "-1"},
					{2, "-2"},
				},
			},
		},
	},
	{
		Name: "NOT EXISTS with nullable filter",
		SetUpScript: []string{
			"CREATE TABLE t0(c0 INT , c1 INT);",
			"INSERT INTO t0(c0, c1) VALUES (1, -2);",
			"create table t1(c0 int, primary key(c0))",
			"insert into t1 values (1)",
			"create table t2(c0 varchar(500), primary key(c0))",
			"insert into t2 values ('9')",
			"create table t3(c0 boolean, primary key(c0))",
			"insert into t3 values(false)",
		},
		Assertions: []ScriptTestAssertion{
			{
				// https://github.com/dolthub/dolt/issues/10070
				Query:    `SELECT * FROM t0 WHERE NOT EXISTS (SELECT 1 FROM (SELECT 1) alias0 WHERE (CASE -1 WHEN t0.c1 THEN false END));`,
				Expected: []sql.Row{{1, -2}},
			},
			{
				// https://github.com/dolthub/dolt/issues/10092
				Query:    "select * from t1 where not exists (select 1 from (select 1) as subquery where weekday(t1.c0))",
				Expected: []sql.Row{{1}},
			},
			{
				// https://github.com/dolthub/dolt/issues/10102
				Query:    "SELECT * FROM t2 WHERE NOT EXISTS (SELECT 1 FROM (SELECT 1) AS sub0 WHERE ASIN(t2.c0));",
				Expected: []sql.Row{{"9"}},
				// Postgres does not allow varchar types as inputs for ASIN
				Dialect: "mysql",
			},
			{
				// https://github.com/dolthub/dolt/issues/10157
				Query:    "SELECT * FROM t3 WHERE NOT EXISTS (SELECT 1 FROM (SELECT 1) AS sub0 WHERE LOG2(t3.c0));",
				Expected: []sql.Row{{0}},
			},
		},
	},
}

var SpatialScriptTests = []ScriptTest{
	{
		Name: "create table using default point value",
		SetUpScript: []string{
			"CREATE TABLE test (i int primary key, p point default (point(123.456, 7.89)));",
			"insert into test (i) values (0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select st_aswkt(p) from test",
				Expected: []sql.Row{{"POINT(123.456 7.89)"}},
			},
			{
				Query:    "show create table test",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `i` int NOT NULL,\n  `p` point DEFAULT (point(123.456,7.89)),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"p", "point", "YES", "", "(point(123.456,7.89))", "DEFAULT_GENERATED"},
				},
			},
		},
	},
	{
		Name: "create table using default linestring value",
		SetUpScript: []string{
			"CREATE TABLE test (i int primary key, l linestring default (linestring(point(1,2), point(3,4))));",
			"insert into test (i) values (0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select st_aswkt(l) from test",
				Expected: []sql.Row{{"LINESTRING(1 2,3 4)"}},
			},
			{
				Query:    "show create table test",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `i` int NOT NULL,\n  `l` linestring DEFAULT (linestring(point(1,2),point(3,4))),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"l", "linestring", "YES", "", "(linestring(point(1,2),point(3,4)))", "DEFAULT_GENERATED"},
				},
			},
		},
	},
	{
		Name: "create table using default polygon value",
		SetUpScript: []string{
			"CREATE TABLE test (i int primary key, p polygon default (polygon(linestring(point(0,0), point(1,1), point(2,2), point(0,0)))));",
			"insert into test (i) values (0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select st_aswkt(p) from test",
				Expected: []sql.Row{{"POLYGON((0 0,1 1,2 2,0 0))"}},
			},
			{
				Query:    "show create table test",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `i` int NOT NULL,\n  `p` polygon DEFAULT (polygon(linestring(point(0,0),point(1,1),point(2,2),point(0,0)))),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"p", "polygon", "YES", "", "(polygon(linestring(point(0,0),point(1,1),point(2,2),point(0,0))))", "DEFAULT_GENERATED"},
				},
			},
		},
	},
	{
		Name: "create geometry table using default point value",
		SetUpScript: []string{
			"CREATE TABLE test (i int primary key, g geometry  default (point(123.456, 7.89)));",
			"insert into test (i) values (0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select st_aswkt(g) from test",
				Expected: []sql.Row{{"POINT(123.456 7.89)"}},
			},
			{
				Query:    "show create table test",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `i` int NOT NULL,\n  `g` geometry DEFAULT (point(123.456,7.89)),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"g", "geometry", "YES", "", "(point(123.456,7.89))", "DEFAULT_GENERATED"},
				},
			},
		},
	},
	{
		Name: "create geometry table using default linestring value",
		SetUpScript: []string{
			"CREATE TABLE test (i int primary key, g geometry default (linestring(point(1,2), point(3,4))));",
			"insert into test (i) values (0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select st_aswkt(g) from test",
				Expected: []sql.Row{{"LINESTRING(1 2,3 4)"}},
			},
			{
				Query:    "show create table test",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `i` int NOT NULL,\n  `g` geometry DEFAULT (linestring(point(1,2),point(3,4))),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"g", "geometry", "YES", "", "(linestring(point(1,2),point(3,4)))", "DEFAULT_GENERATED"},
				},
			},
		},
	},
	{
		Name: "create geometry table using default polygon value",
		SetUpScript: []string{
			"CREATE TABLE test (i int primary key, g geometry default (polygon(linestring(point(0,0), point(1,1), point(2,2), point(0,0)))));",
			"insert into test (i) values (0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select st_aswkt(g) from test",
				Expected: []sql.Row{{"POLYGON((0 0,1 1,2 2,0 0))"}},
			},
			{
				Query:    "show create table test",
				Expected: []sql.Row{{"test", "CREATE TABLE `test` (\n  `i` int NOT NULL,\n  `g` geometry DEFAULT (polygon(linestring(point(0,0),point(1,1),point(2,2),point(0,0)))),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query: "describe test",
				Expected: []sql.Row{
					{"i", "int", "NO", "PRI", nil, ""},
					{"g", "geometry", "YES", "", "(polygon(linestring(point(0,0),point(1,1),point(2,2),point(0,0))))", "DEFAULT_GENERATED"}},
			},
		},
	},
	{
		Name: "create table with NULL default values for geometry types",
		SetUpScript: []string{
			"CREATE TABLE null_default (pk int NOT NULL PRIMARY KEY, v1 geometry DEFAULT NULL, v2 linestring DEFAULT NULL, v3 point DEFAULT NULL, v4 polygon DEFAULT NULL)",
			"insert into null_default(pk) values (0)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from null_default",
				Expected: []sql.Row{{0, nil, nil, nil, nil}},
			},
		},
	},
	{
		Name: "create table using SRID value for geometry type",
		SetUpScript: []string{
			"CREATE TABLE tab0 (i int primary key, g geometry srid 4326 default (point(1,1)));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "show create table tab0",
				Expected: []sql.Row{{"tab0", "CREATE TABLE `tab0` (\n  `i` int NOT NULL,\n  `g` geometry /*!80003 SRID 4326 */ DEFAULT (point(1,1)),\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "INSERT INTO tab0 VALUES (1, ST_GEOMFROMTEXT(ST_ASWKT(POINT(1,2)), 4326))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select i, ST_ASWKT(g) FROM tab0",
				Expected: []sql.Row{{1, "POINT(1 2)"}},
			},
			{
				Query:       "INSERT INTO tab0 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(POINT(2,4))))",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "INSERT INTO tab0 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(LINESTRING(POINT(1, 6),POINT(4, 3))), 4326))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select i, ST_ASWKT(g) FROM tab0",
				Expected: []sql.Row{{1, "POINT(1 2)"}, {2, "LINESTRING(1 6,4 3)"}},
			},
		},
	},
	{
		Name: "create table using SRID value for linestring type",
		SetUpScript: []string{
			"CREATE TABLE tab1 (i int primary key, l linestring srid 0);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "show create table tab1",
				Expected: []sql.Row{{"tab1", "CREATE TABLE `tab1` (\n  `i` int NOT NULL,\n  `l` linestring /*!80003 SRID 0 */,\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "INSERT INTO tab1 VALUES (1, LINESTRING(POINT(0, 0),POINT(2, 2)))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select i, ST_ASWKT(l) FROM tab1",
				Expected: []sql.Row{{1, "LINESTRING(0 0,2 2)"}},
			},
			{
				Query:       "INSERT INTO tab1 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(LINESTRING(POINT(1, 6),POINT(4, 3))), 4326))",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "select i, ST_ASWKT(l) FROM tab1",
				Expected: []sql.Row{{1, "LINESTRING(0 0,2 2)"}},
			},
		},
	},
	{
		Name: "create table using SRID value for point type",
		SetUpScript: []string{
			"CREATE TABLE tab2 (i int primary key);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "ALTER TABLE tab2 ADD COLUMN p POINT NOT NULL SRID 0",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "show create table tab2",
				Expected: []sql.Row{{"tab2", "CREATE TABLE `tab2` (\n  `i` int NOT NULL,\n  `p` point NOT NULL /*!80003 SRID 0 */,\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "INSERT INTO tab2 VALUES (1, POINT(2, 2))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select i, ST_ASWKT(p) FROM tab2",
				Expected: []sql.Row{{1, "POINT(2 2)"}},
			},
			{
				Query:       "INSERT INTO tab2 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(POINT(1, 6)), 4326))",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "select i, ST_ASWKT(p) FROM tab2",
				Expected: []sql.Row{{1, "POINT(2 2)"}},
			},
			{
				Query:    "ALTER TABLE tab2 CHANGE COLUMN p p POINT NOT NULL",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "INSERT INTO tab2 VALUES (2, ST_GEOMFROMTEXT(ST_ASWKT(POINT(1, 6)), 4326))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select i, ST_ASWKT(p) FROM tab2",
				Expected: []sql.Row{{1, "POINT(2 2)"}, {2, "POINT(1 6)"}},
			},
			{
				Query:       "ALTER TABLE tab2 CHANGE COLUMN p p POINT NOT NULL SRID 4326",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "delete from tab2 where i = 1",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "ALTER TABLE tab2 CHANGE COLUMN p p POINT NOT NULL SRID 4326",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "show create table tab2",
				Expected: []sql.Row{{"tab2", "CREATE TABLE `tab2` (\n  `i` int NOT NULL,\n  `p` point NOT NULL /*!80003 SRID 4326 */,\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
		},
	},
	{
		Name: "create table using SRID value for polygon type",
		SetUpScript: []string{
			"CREATE TABLE tab3 (i int primary key, y polygon NOT NULL);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "show create table tab3",
				Expected: []sql.Row{{"tab3", "CREATE TABLE `tab3` (\n  `i` int NOT NULL,\n  `y` polygon NOT NULL,\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "INSERT INTO tab3 VALUES (1, polygon(linestring(point(0,0),point(8,0),point(12,9),point(0,9),point(0,0))))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "select i, ST_ASWKT(y) FROM tab3",
				Expected: []sql.Row{{1, "POLYGON((0 0,8 0,12 9,0 9,0 0))"}},
			},
			{
				Query:    "ALTER TABLE tab3 MODIFY COLUMN y POLYGON NOT NULL SRID 0",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:       "ALTER TABLE tab3 MODIFY COLUMN y POLYGON NOT NULL SRID 4326",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "select i, ST_ASWKT(y) FROM tab3",
				Expected: []sql.Row{{1, "POLYGON((0 0,8 0,12 9,0 9,0 0))"}},
			},
			{
				Query:    "ALTER TABLE tab3 MODIFY COLUMN y GEOMETRY NULL SRID 0",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "select i, ST_ASWKT(y) FROM tab3",
				Expected: []sql.Row{{1, "POLYGON((0 0,8 0,12 9,0 9,0 0))"}},
			},
		},
	},
	{
		Name: "invalid cases of SRID value",
		SetUpScript: []string{
			"CREATE TABLE table1 (i int primary key, p point srid 4326);",
			"INSERT INTO table1 VALUES (1, ST_SRID(POINT(1, 5), 4326))",
			"CREATE TABLE table2 (i int primary key, g geometry /*!80003 SRID 3857*/);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "CREATE TABLE table3 (i int primary key, p point srid 1);",
				ExpectedErr: sql.ErrNoSRID,
			},
			{
				Query:    "CREATE TABLE table3 (i int primary key, p point srid 3857);",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "show create table table2",
				Expected: []sql.Row{
					{"table2", "CREATE TABLE `table2` (\n  `i` int NOT NULL,\n  `g` geometry /*!80003 SRID 3857 */,\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query:    "SELECT i, ST_ASWKT(p) FROM table1;",
				Expected: []sql.Row{{1, "POINT(5 1)"}},
			},
			{
				Query:       "INSERT INTO table1 VALUES (2, POINT(2, 5))",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "SELECT i, ST_ASWKT(p) FROM table1;",
				Expected: []sql.Row{{1, "POINT(5 1)"}},
			},
			{
				Query:       "ALTER TABLE table1 CHANGE COLUMN p p linestring srid 4326",
				ExpectedErr: sql.ErrSpatialTypeConversion,
			},
			{
				Query:       "ALTER TABLE table1 CHANGE COLUMN p p geometry srid 0",
				ExpectedErr: sql.ErrNotMatchingSRIDWithColName,
			},
			{
				Query:    "ALTER TABLE table1 CHANGE COLUMN p p geometry srid 4326",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "show create table table1",
				Expected: []sql.Row{{"table1", "CREATE TABLE `table1` (\n  `i` int NOT NULL,\n  `p` geometry /*!80003 SRID 4326 */,\n  PRIMARY KEY (`i`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
			},
			{
				Query:    "INSERT INTO table1 VALUES (2, ST_SRID(LINESTRING(POINT(0, 0),POINT(2, 2)),4326))",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:       "ALTER TABLE table1 CHANGE COLUMN p p point srid 4326",
				ExpectedErr: sql.ErrSpatialTypeConversion,
			},
		},
	},
}

var SpatialIndexScriptTests = []ScriptTest{
	{
		Name:        "create spatial index errors",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "create table geom(g geometry, SPATIAL INDEX(g))",
				ExpectedErr: sql.ErrNullableSpatialIdx,
			},
			{
				Query:       "create table geom(g geometry SRID 4326, SPATIAL INDEX(g))",
				ExpectedErr: sql.ErrNullableSpatialIdx,
			},
			{
				Query:       "create table geom(g1 geometry NOT NULL SRID 0, g2 geometry NOT NULL SRID 4326, SPATIAL INDEX(g1, g2))",
				ExpectedErr: sql.ErrTooManyKeyParts,
			},
		},
	},
	{
		Name: "alter table spatial index nullable",
		SetUpScript: []string{
			"create table geom(g geometry)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "alter table geom add spatial index (g)",
				ExpectedErr: sql.ErrNullableSpatialIdx,
			},
		},
	},
	{
		Name: "alter table spatial index with srid nullable",
		SetUpScript: []string{
			"create table geom(g geometry SRID 4326)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "alter table geom add spatial index (g)",
				ExpectedErr: sql.ErrNullableSpatialIdx,
			},
		},
	},
	{
		Name: "show table with spatial indexes",
		SetUpScript: []string{
			"create table geom(" +
				"p point not null srid 0," +
				"l linestring not null srid 0," +
				"py polygon not null srid 0," +
				"mp multipoint not null srid 0," +
				"ml multilinestring not null srid 0," +
				"mpy multipolygon not null srid 0," +
				"gc geometrycollection not null srid 0," +
				"g geometry not null srid 0)",
			"alter table geom add spatial index (p)",
			"alter table geom add spatial index (l)",
			"alter table geom add spatial index (py)",
			"alter table geom add spatial index (mp)",
			"alter table geom add spatial index (ml)",
			"alter table geom add spatial index (mpy)",
			"alter table geom add spatial index (gc)",
			"alter table geom add spatial index (g)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "show create table geom",
				Expected: []sql.Row{
					{
						"geom",
						"CREATE TABLE `geom` (\n" +
							"  `p` point NOT NULL /*!80003 SRID 0 */,\n" +
							"  `l` linestring NOT NULL /*!80003 SRID 0 */,\n" +
							"  `py` polygon NOT NULL /*!80003 SRID 0 */,\n" +
							"  `mp` multipoint NOT NULL /*!80003 SRID 0 */,\n" +
							"  `ml` multilinestring NOT NULL /*!80003 SRID 0 */,\n" +
							"  `mpy` multipolygon NOT NULL /*!80003 SRID 0 */,\n" +
							"  `gc` geometrycollection NOT NULL /*!80003 SRID 0 */,\n" +
							"  `g` geometry NOT NULL /*!80003 SRID 0 */,\n" +
							"  SPATIAL KEY `g` (`g`),\n" +
							"  SPATIAL KEY `gc` (`gc`),\n" +
							"  SPATIAL KEY `l` (`l`),\n" +
							"  SPATIAL KEY `ml` (`ml`),\n" +
							"  SPATIAL KEY `mp` (`mp`),\n" +
							"  SPATIAL KEY `mpy` (`mpy`),\n" +
							"  SPATIAL KEY `p` (`p`),\n" +
							"  SPATIAL KEY `py` (`py`)\n" +
							") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin",
					},
				},
			},
		},
	},
	{
		Name: "add spatial index to non-empty table",
		SetUpScript: []string{
			"create table geom_tbl(g geometry not null srid 0)",
			"insert into geom_tbl values (point(0,0)), (linestring(point(1,1), point(2,2)))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "alter table geom_tbl add spatial index (g)",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "show create table geom_tbl",
				Expected: []sql.Row{
					{"geom_tbl", "CREATE TABLE `geom_tbl` (\n  `g` geometry NOT NULL /*!80003 SRID 0 */,\n  SPATIAL KEY `g` (`g`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "select count(*) from geom_tbl where st_intersects(g, st_geomfromtext('polygon((0 0,0 10,10 10,10 0,0 0))'))",
				Expected: []sql.Row{
					{2},
				},
			},
		},
	},
	{
		Name: "add spatial index to non-empty table with primary key",
		SetUpScript: []string{
			"create table geom_tbl(i int, j int, g geometry not null srid 0, primary key (i, j))",
			"insert into geom_tbl values (1, 10, point(0,0)), (2, 20, linestring(point(1,1), point(2,2)))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "alter table geom_tbl add spatial index (g)",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
			{
				Query: "show create table geom_tbl",
				Expected: []sql.Row{
					{"geom_tbl", "CREATE TABLE `geom_tbl` (\n  `i` int NOT NULL,\n  `j` int NOT NULL,\n  `g` geometry NOT NULL /*!80003 SRID 0 */,\n  PRIMARY KEY (`i`,`j`),\n  SPATIAL KEY `g` (`g`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
				},
			},
			{
				Query: "select count(*) from geom_tbl where st_intersects(g, st_geomfromtext('polygon((0 0,0 10,10 10,10 0,0 0))'))",
				Expected: []sql.Row{
					{2},
				},
			},
		},
	},
	{
		Name: "spatial indexes do not work as foreign keys",
		SetUpScript: []string{
			"create table parent (i int primary key, p point not null srid 0, spatial index (p))",
			"create table child1 (j int primary key, p point not null srid 0, spatial index (p))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "alter table child1 add foreign key (p) references parent (p)",
				ExpectedErr: sql.ErrForeignKeyMissingReferenceIndex,
			},
			{
				Query:       "create table child2 (p point not null srid 0, spatial index (p), foreign key (p) references parent (p))",
				ExpectedErr: sql.ErrForeignKeyMissingReferenceIndex,
			},
		},
	},
}

var PreparedScriptTests = []ScriptTest{
	{
		Name: "table_count optimization refreshes result",
		SetUpScript: []string{
			"create table a (a int primary key);",
			"insert into a values (0), (1), (2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "prepare cnt from 'select count(*) from a';",
				Expected: []sql.Row{{types.OkResult{Info: plan.PrepareInfo{}}}},
			},
			{
				Query:    "execute cnt",
				Expected: []sql.Row{{3}},
			},
			{
				Query: "insert into a values (3), (4)",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 2}},
				},
			},
			{
				Query:    "execute cnt",
				Expected: []sql.Row{{5}},
			},
		},
	},
	{
		Name:        "bad prepare",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:          "prepare s from 'prepare t from ?'",
				ExpectedErrStr: "syntax error at position 17 near ':v1'",
			},
			{
				Query:          "prepare s from 'a very real query'",
				ExpectedErrStr: "syntax error at position 2 near 'a'",
			},
			{
				Query:       "deallocate prepare idontexist",
				ExpectedErr: sql.ErrUnknownPreparedStatement,
			},
		},
	},
	{
		Name:        "simple select case no bindings",
		SetUpScript: []string{},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "execute s",
				ExpectedErr: sql.ErrUnknownPreparedStatement,
			},
			{
				Query: "prepare s from 'select 1'",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query: "execute s",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "deallocate prepare s",
				Expected: []sql.Row{
					{types.OkResult{}},
				},
			},
			{
				Query:       "execute s",
				ExpectedErr: sql.ErrUnknownPreparedStatement,
			},
		},
	},
	{
		Name: "simple select case one binding",
		SetUpScript: []string{
			"set @a = 1",
			"set @b = 100",
			"set @c = 'abc'",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "prepare s from 'select ?'",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query:          "execute s",
				ExpectedErrStr: "bind variable not provided: 'v1'",
			},
			{
				Query: "execute s using @abc",
				Expected: []sql.Row{
					{nil},
				},
			},
			{
				Query:          "execute s using @a, @b, @c, @abc",
				ExpectedErrStr: "invalid arguments. expected: 1, found: 4",
			},
			{
				Query: "execute s using @a",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "execute s using @b",
				Expected: []sql.Row{
					{100},
				},
			},
			{
				Query: "execute s using @c",
				Expected: []sql.Row{
					{"abc"},
				},
			},
			{
				Query: "deallocate prepare s",
				Expected: []sql.Row{
					{types.OkResult{}},
				},
			},
			{
				Query:       "execute s using @a",
				ExpectedErr: sql.ErrUnknownPreparedStatement,
			},
		},
	},
	{
		Name: "prepare with time type binding",
		SetUpScript: []string{
			"create table t (d date, dt datetime, t time, ts timestamp);",
			"set @d = date('2001-02-03');",
			"set @dt = datetime('2001-02-03 12:34:56');",
			"set @t = time('12:34:56');",
			"set @ts = timestamp('2001-02-03 12:34:56');",
			"prepare s from 'select ?';",
			"prepare sd from 'insert into t(d) values(?)';",
			"prepare sdt from 'insert into t(dt) values(?)';",
			"prepare st from 'insert into t(t) values(?)';",
			"prepare sts from 'insert into t(ts) values(?)';",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "execute s using @d;",
				Expected: []sql.Row{
					{"2001-02-03"},
				},
			},
			{
				Query: "execute s using @dt;",
				Expected: []sql.Row{
					{time.Date(2001, time.February, 3, 12, 34, 56, 0, time.UTC)},
				},
			},
			{
				// types.Timespan not supported as bindvar
				Skip:  true,
				Query: "execute s using @t;",
				Expected: []sql.Row{
					{"12:34:56"},
				},
			},
			{
				Query: "execute s using @ts;",
				Expected: []sql.Row{
					{time.Date(2001, time.February, 3, 12, 34, 56, 0, time.UTC)},
				},
			},
			{
				SkipResultCheckOnServerEngine: true,
				Query:                         "execute sd using @d;",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				SkipResultCheckOnServerEngine: true,
				Query:                         "execute sdt using @dt;",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				// types.Timespan not supported as bindvar
				Skip:                          true,
				SkipResultCheckOnServerEngine: true,
				Query:                         "execute st using @t;",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				SkipResultCheckOnServerEngine: true,
				Query:                         "execute sts using @ts;",
				Expected: []sql.Row{
					{types.NewOkResult(1)},
				},
			},
			{
				// TODO: should also select t when we fix that
				Query: "select d, dt, ts from t",
				Expected: []sql.Row{
					{time.Date(2001, time.February, 3, 0, 0, 0, 0, time.UTC), nil, nil},
					{nil, time.Date(2001, time.February, 3, 12, 34, 56, 0, time.UTC), nil},
					{nil, nil, time.Date(2001, time.February, 3, 12, 34, 56, 0, time.UTC)},
				},
			},
		},
	},
	{
		Name: "prepare with decimal type binding",
		SetUpScript: []string{
			"create table t (d decimal);",
			"set @d = cast(123.45 as Decimal(5,2));",
			"prepare s from 'select ?';",
			"prepare sd from 'insert into t values(?)';",
		},
		Assertions: []ScriptTestAssertion{
			{
				Skip:  true,
				Query: "execute s using @d;",
				Expected: []sql.Row{
					{"123.45"},
				},
			},
			{
				Skip:                          true,
				SkipResultCheckOnServerEngine: true,
				Query:                         "execute sd using @d;",
				Expected: []sql.Row{
					{"123.45"},
				},
			},
			{
				Skip:  true,
				Query: "select * from t",
				Expected: []sql.Row{
					{"123.45"},
				},
			},
		},
	},
	{
		Name: "prepare insert",
		SetUpScript: []string{
			"set @a = 123",
			"set @b = 'abc'",
			"create table t (i int, j varchar(100))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "prepare s from 'insert into t values (?,?)'",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query:          "execute s using @a",
				ExpectedErrStr: "bind variable not provided: 'v2'",
			},
			{
				SkipResultCheckOnServerEngine: true, // execute depends on prepare stmt for whether to use 'query' or 'exec' from go sql driver.
				Query:                         "execute s using @a, @b",
				Expected: []sql.Row{
					{types.OkResult{RowsAffected: 1}},
				},
			},
			{
				Query: "select * from t order by i",
				Expected: []sql.Row{
					{123, "abc"},
				},
			},
			{
				Query: "deallocate prepare s",
				Expected: []sql.Row{
					{types.OkResult{}},
				},
			},
			{
				Query:       "execute s using @a",
				ExpectedErr: sql.ErrUnknownPreparedStatement,
			},
		},
	},
	{
		Name: "prepare using user vars",
		SetUpScript: []string{
			"create table t (i int primary key);",
			"insert into t values (0), (1), (2);",
			"set @num = 123",
			"set @bad = 'bad'",
			"set @a = 'select * from t order by i'",
			"set @b = concat('select 1',' + 1')",
			"set @c = 'select 1 from dual limit ?'",
			"set @d = 'select @num'",
		},
		Assertions: []ScriptTestAssertion{
			{
				// non-existent vars is the same as preparing with NULL
				Query:          "prepare stmt from @asdf",
				ExpectedErrStr: "syntax error at position 5 near 'NULL'",
			},
			{
				Query:          "prepare stmt from @num",
				ExpectedErrStr: "syntax error at position 4 near '123'",
			},
			{
				Query:          "prepare stmt from @bad",
				ExpectedErrStr: "syntax error at position 4 near 'bad'",
			},
			{
				Query: "prepare stmt from @a",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query: "execute stmt",
				Expected: []sql.Row{
					{0},
					{1},
					{2},
				},
			},
			{
				Query: "prepare stmt from @b",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query: "execute stmt",
				Expected: []sql.Row{
					{2},
				},
			},
			{
				Query: "prepare stmt from @c",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query: "execute stmt using @num",
				Expected: []sql.Row{
					{1},
				},
			},
			{
				Query: "prepare stmt from @d",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query: "execute stmt",
				Expected: []sql.Row{
					{123},
				},
			},
		},
	},
	{
		Name: "Complex join query with foreign key constraints",
		SetUpScript: []string{
			"CREATE TABLE `users` (`id` int NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, PRIMARY KEY (`id`));",
			"CREATE TABLE `tweet` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `content` text NOT NULL, `timestamp` bigint NOT NULL, PRIMARY KEY (`id`), KEY `tweet_user_id` (`user_id`), CONSTRAINT `0qpfesgd` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`));",
			"INSERT INTO `users` (`id`,`username`) VALUES (1,'huey'), (2,'zaizee'), (3,'mickey');",
			"INSERT INTO `tweet` (`id`,`user_id`,`content`,`timestamp`) VALUES (1,1,'meow',1647463727), (2,1,'purr',1647463727), (3,2,'hiss',1647463727), (4,3,'woof',1647463727);",
			"set @u2 = 'u2';",
			"set @u3 = 'u3';",
			"set @u4 = 'u4';",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "prepare s from 'SELECT `t1`.`username`, COUNT(`t1`.`id`) AS `ct` FROM ((SELECT `t2`.`id`, `t2`.`content`, `t3`.`username` FROM `tweet` AS `t2` INNER JOIN `users` AS `t3` ON (`t2`.`user_id` = `t3`.`id`) WHERE (`t3`.`username` = ?)) UNION (SELECT `t4`.`id`, `t4`.`content`, `t5`.`username` FROM `tweet` AS `t4` INNER JOIN `users` AS `t5` ON (`t4`.`user_id` = `t5`.`id`) WHERE (`t5`.`username` IN (?, ?)))) AS `t1` GROUP BY `t1`.`username` ORDER BY COUNT(`t1`.`id`) DESC'",
				Expected: []sql.Row{
					{types.OkResult{Info: plan.PrepareInfo{}}},
				},
			},
			{
				Query:    "execute s using @u3, @u2, @u4",
				Expected: []sql.Row{},
			},
		},
	},
	{
		Name: "Drop column with check constraint, no other columns",
		SetUpScript: []string{
			"create table mytable (pk int primary key);",
			"ALTER TABLE mytable ADD COLUMN col2 text NOT NULL;",
			"ALTER TABLE mytable ADD CONSTRAINT constraint_check CHECK (col2 LIKE '%myregex%');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "ALTER TABLE mytable DROP COLUMN col2",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
		},
	},
	{
		Name: "Drop column with check constraint, other column referenced first",
		SetUpScript: []string{
			"create table mytable (pk int primary key);",
			"ALTER TABLE mytable ADD COLUMN col2 text NOT NULL;",
			"ALTER TABLE mytable ADD COLUMN col3 text NOT NULL;",
			"ALTER TABLE mytable ADD CONSTRAINT constraint_check CHECK (col3 LIKE col2);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "ALTER TABLE mytable DROP COLUMN col2",
				ExpectedErr: sql.ErrCheckConstraintInvalidatedByColumnAlter,
			},
		},
	},
	{
		Name: "Drop column with check constraint, other column referenced second",
		SetUpScript: []string{
			"create table mytable (pk int primary key);",
			"ALTER TABLE mytable ADD COLUMN col2 text NOT NULL;",
			"ALTER TABLE mytable ADD COLUMN col3 text NOT NULL;",
			"ALTER TABLE mytable ADD CONSTRAINT constraint_check CHECK (col2 LIKE col3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "ALTER TABLE mytable DROP COLUMN col2",
				ExpectedErr: sql.ErrCheckConstraintInvalidatedByColumnAlter,
			},
		},
	},
	{
		Name: "Drop column with check constraint, multiple constraints",
		SetUpScript: []string{
			"create table mytable (pk int primary key);",
			"ALTER TABLE mytable ADD COLUMN col2 text NOT NULL;",
			"ALTER TABLE mytable ADD COLUMN col3 text NOT NULL;",
			"ALTER TABLE mytable ADD CONSTRAINT ok_check CHECK (col2 LIKE '%myregex%');",
			"ALTER TABLE mytable ADD CONSTRAINT bad_check CHECK (col2 LIKE col3);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "ALTER TABLE mytable DROP COLUMN col2",
				ExpectedErr: sql.ErrCheckConstraintInvalidatedByColumnAlter,
			},
		},
	},
	{
		// https://github.com/dolthub/dolthub-issues/issues/489
		Name: "Large character data",
		SetUpScript: []string{
			"CREATE TABLE `test` (`id` int NOT NULL AUTO_INCREMENT, `data` blob NOT NULL, PRIMARY KEY (`id`))",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: `INSERT INTO test (data) values (?)`,
				Bindings: map[string]sqlparser.Expr{
					// Vitess chooses VARBINARY as the bindvar type if the client sends CHAR data
					// If we change how Vitess interprets client bindvar types, we should update this test
					// Or better yet: have a test harness that uses the server directly
					"v1": sqlparser.NewStrVal([]byte(
						"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
							"")),
				},
				Expected: []sql.Row{{types.OkResult{
					RowsAffected: 1,
					InsertID:     1,
				}}},
			},
		},
	},
}

var BrokenScriptTests = []ScriptTest{
	{
		Name: "ALTER TABLE RENAME on a column when another column has a default dependency on it",
		SetUpScript: []string{
			"CREATE TABLE `test` (`pk` bigint NOT NULL,`v2` int NOT NULL DEFAULT '100',`v3` int DEFAULT ((`v2` + 1)),PRIMARY KEY (`pk`));",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:       "alter table test rename column v2 to mycol",
				ExpectedErr: sql.ErrAlterTableNotSupported, // Not the correct error. The point is that this query needs to fail.
			},
		},
	},
	// TODO: We should implement unique indexes with GMS
	{
		Name: "Keyless Table with Unique Index",
		SetUpScript: []string{
			"create table a (x int, val int unique)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "INSERT INTO a VALUES (1, 1)",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:       "INSERT INTO a VALUES (1, 1)",
				ExpectedErr: sql.ErrUniqueKeyViolation,
			},
		},
	},
	{
		Name: "Multialter DDL with ADD/DROP Primary Key",
		SetUpScript: []string{
			"CREATE TABLE t(pk int primary key, v1 int)",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "ALTER TABLE t ADD COLUMN (v2 int), drop primary key, add primary key (v2)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				Query:       "ALTER TABLE t ADD COLUMN (v3 int), drop primary key, add primary key (notacolumn)",
				ExpectedErr: sql.ErrKeyColumnDoesNotExist,
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "", nil, ""},
					{"v1", "int", "YES", "", nil, ""},
					{"v2", "int", "NO", "PRI", nil, ""},
				},
			},
			{
				// This last modification ends up with a UNIQUE constraint on pk
				// This is caused by Table.dropColumnFromSchema, not dropping the pkOrdinal, but this causes other problems specific to GMS
				Query:    "ALTER TABLE t ADD column `v4` int NOT NULL, ADD column `v5` int NOT NULL, DROP COLUMN `v1`, ADD COLUMN `v6` int NOT NULL, DROP COLUMN `v2`, ADD COLUMN v7 int NOT NULL",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query: "DESCRIBE t",
				Expected: []sql.Row{
					{"pk", "int", "NO", "", nil, ""},
					{"v4", "int", "NO", "", nil, ""},
					{"v5", "int", "NO", "", nil, ""},
					{"v6", "int", "NO", "", nil, ""},
					{"v7", "int", "NO", "", nil, ""},
				},
			},
		},
	},
	{
		Name: "REGEXP operator",
		SetUpScript: []string{
			"CREATE TABLE IF NOT EXISTS `person` (`id` INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` VARCHAR(255) NOT NULL);",
			"INSERT INTO `person` (`name`) VALUES ('n1'), ('n2'), ('n3')",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT `t1`.`id`, `t1`.`name` FROM `person` AS `t1` WHERE (`t1`.`name` REGEXP 'N[1,3]') ORDER BY `t1`.`name`;",
				Expected: []sql.Row{{1, "n1"}, {3, "n3"}},
			},
		},
	},
	{
		Name: "non-existent procedure in trigger body",
		SetUpScript: []string{
			"CREATE TABLE XA(YW VARCHAR(24) NOT NULL, XB VARCHAR(100), XC VARCHAR(2500),\n  XD VARCHAR(2500), XE VARCHAR(100), XF VARCHAR(100), XG VARCHAR(100),\n  XI VARCHAR(100), XJ VARCHAR(100), XK VARCHAR(100), XL VARCHAR(100),\n  XM VARCHAR(1000), XN TEXT, XO TEXT, PRIMARY KEY (YW));",
			"CREATE TABLE XP(YW VARCHAR(24) NOT NULL, XQ VARCHAR(100) NOT NULL,\n  XR VARCHAR(1000), PRIMARY KEY (YW));",
			"CREATE TABLE XS(YW VARCHAR(24) NOT NULL, XT VARCHAR(24) NOT NULL,\n  XU VARCHAR(24), XV VARCHAR(100) NOT NULL, XW DOUBLE NOT NULL,\n  XX DOUBLE NOT NULL, XY VARCHAR(100), XC VARCHAR(100), XZ VARCHAR(100) NOT NULL,\n  YA DOUBLE, YB VARCHAR(24) NOT NULL, YC VARCHAR(1000), XO VARCHAR(1000),\n  YD DOUBLE NOT NULL, YE DOUBLE NOT NULL, PRIMARY KEY (YW));",
			"CREATE TABLE YF(YW VARCHAR(24) NOT NULL, XB VARCHAR(100) NOT NULL, YG VARCHAR(100),\n  YH VARCHAR(100), XO TEXT, PRIMARY KEY (YW));",
			"CREATE TABLE yp(YW VARCHAR(24) NOT NULL, XJ VARCHAR(100) NOT NULL, XL VARCHAR(100),\n  XT VARCHAR(24) NOT NULL, YI INT NOT NULL, XO VARCHAR(1000), PRIMARY KEY (YW),\n  FOREIGN KEY (XT) REFERENCES XP (YW));",
			"INSERT INTO XS VALUES ('', '', NULL, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC', 0, 0,\n  NULL, NULL, '', NULL, '', NULL, NULL, 0, 0);",
			"INSERT INTO YF VALUES ('', '', NULL, NULL, NULL);",
			"INSERT INTO XA VALUES ('', '', '', '', '', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC',\n  '', '', '', '', '', '', '', '');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SELECT DISTINCT YM.YW AS YW,\n  (SELECT YW FROM YF WHERE YF.XB = YM.XB) AS YF_YW,\n  (\n    SELECT YW\n    FROM yp\n    WHERE\n      yp.XJ = YM.XJ AND\n      (yp.XL = YM.XL OR (yp.XL IS NULL AND YM.XL IS NULL)) AND\n      yp.XT = nd.XT\n    ) AS YJ,\n  XE AS XE,\n  XI AS YO,\n  XK AS XK,\n  XM AS XM,\n  CASE\n    WHEN YM.XO <> 'Z'\n  THEN YM.XO\n  ELSE NULL\n  END AS XO\n  FROM (\n    SELECT YW, XB, XC, XE, XF, XI, XJ, XK,\n      CASE WHEN XL = 'Z' OR XL = 'Z' THEN NULL ELSE XL END AS XL,\n      XM, XO\n    FROM XA\n  ) YM\n  INNER JOIN XS nd\n    ON nd.XV = XF\n  WHERE\n    XB IN (SELECT XB FROM YF) AND\n    (XF IS NOT NULL AND XF <> 'Z')\n  UNION\n  SELECT DISTINCT YL.YW AS YW,\n    (\n      SELECT YW\n      FROM YF\n      WHERE YF.XB = YL.XB\n    ) AS YF_YW,\n    (\n      SELECT YW FROM yp\n      WHERE\n        yp.XJ = YL.XJ AND\n        (yp.XL = YL.XL OR (yp.XL IS NULL AND YL.XL IS NULL)) AND\n        yp.XT = YN.XT\n    ) AS YJ,\n    XE AS XE,\n    XI AS YO,\n    XK AS XK,\n    XM AS XM,\n    CASE WHEN YL.XO <> 'Z' THEN YL.XO ELSE NULL END AS XO\n  FROM (\n    SELECT YW, XB, XC, XE, XF, XI, XJ, XK,\n      CASE WHEN XL = 'Z' OR XL = 'Z' THEN NULL ELSE XL END AS XL,\n      XM, XO\n      FROM XA\n  ) YL\n  INNER JOIN XS YN\n    ON YN.XC = YL.XC\n  WHERE\n    XB IN (SELECT XB FROM YF) AND \n    (XF IS NULL OR XF = 'Z');",
				Expected: []sql.Row{{"", "", "", "", "", "", "", ""}},
			},
		},
	},
	{
		Name: "non-existent procedure in trigger body",
		SetUpScript: []string{
			"create table tbl_I (i int primary key);",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query: "alter table tbl_i add column j int, add check (j < 10);",
				Expected: []sql.Row{
					{types.NewOkResult(0)},
				},
			},
		},
	},
	{
		Name: "renaming table name that is referenced in existing view",
		SetUpScript: []string{
			"create table t1 (id int primary key, v1 int);",
			"insert into t1 values (1,1);",
			"create view v1 as select * from t1;",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "select * from v1;",
				Expected: []sql.Row{{1, 1}},
			},
			{
				Query:    "rename table t1 to t2;",
				Expected: []sql.Row{{types.OkResult{}}},
			},
			{
				Query:    "show tables;",
				Expected: []sql.Row{{"myview"}, {"t2"}, {"v1"}},
			},
			{
				Query:          "select * from v1;",
				ExpectedErrStr: "View 'v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them",
			},
			{
				Query:                 "show create view v1;",
				Expected:              []sql.Row{{"v1", "CREATE VIEW `v1` AS select * from t1", "utf8mb4", "utf8mb4_0900_bin"}},
				ExpectedWarningsCount: 1,
			},
			{
				Query:    "show warnings;",
				Expected: []sql.Row{{"Warning", 1356, "View 'v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"}},
			},
		},
	},
	{
		Name: "TIMESTAMP type value should be converted from session TZ to UTC TZ to be stored",
		SetUpScript: []string{
			"CREATE TABLE timezone_test (ts TIMESTAMP, dt DATETIME)",
			"INSERT INTO timezone_test VALUES ('2023-02-14 08:47', '2023-02-14 08:47');",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "SET SESSION time_zone = '-05:00';",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SELECT DATE_FORMAT(ts, '%H:%i:%s'), DATE_FORMAT(dt, '%H:%i:%s') from timezone_test;",
				Expected: []sql.Row{{"11:47:00", "08:47:00"}},
			},
			{
				Query:    "SELECT UNIX_TIMESTAMP(ts), UNIX_TIMESTAMP(dt) from timezone_test;",
				Expected: []sql.Row{{float64(1676393220), float64(1676382420)}},
			},
		},
	},
}

var CreateDatabaseScripts = []ScriptTest{
	{
		// https://github.com/dolthub/dolt/pull/9830
		Name: "CREATE SCHEMA without database selection falls back to CREATE DATABASE",
		SetUpScript: []string{
			"CREATE DATABASE tmp",
			"USE tmp",
		},
		Dialect: "mysql",
		Assertions: []ScriptTestAssertion{
			{
				Query: "DROP DATABASE tmp",
			},
			{
				Query:    "CREATE SCHEMA NewDatabase",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "SHOW DATABASES",
				Expected: []sql.Row{{"NewDatabase"}, {"information_schema"}, {"mydb"}, {"mysql"}},
			},
			{
				Query:    "USE NewDatabase",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{"NewDatabase"}},
			},
			{
				Query:    "CREATE TABLE test_table (id INT PRIMARY KEY)",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "SHOW TABLES",
				Expected: []sql.Row{{"test_table"}},
			},
			{
				Query:    "USE mydb",
				Expected: []sql.Row{},
			},
			{
				Query:    "DROP DATABASE NewDatabase",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
		},
	},
	{
		Name: "CREATE DATABASE and create table",
		Assertions: []ScriptTestAssertion{
			{
				Query:    "CREATE DATABASE testdb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "USE testdb",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{"testdb"}},
			},
			{
				Query:    "CREATE TABLE test (pk int primary key)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SHOW TABLES",
				Expected: []sql.Row{{"test"}},
			},
		},
	},
	{
		Name: "CREATE DATABASE IF NOT EXISTS",
		Assertions: []ScriptTestAssertion{
			{
				Query:    "CREATE DATABASE IF NOT EXISTS testdb2",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "USE testdb2",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{"testdb2"}},
			},
			{
				Query:    "CREATE TABLE test (pk int primary key)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SHOW TABLES",
				Expected: []sql.Row{{"test"}},
			},
		},
	},
	{
		Name: "CREATE SCHEMA",
		Assertions: []ScriptTestAssertion{
			{
				Query:    "CREATE SCHEMA testdb3",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "USE testdb3",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{"testdb3"}},
			},
			{
				Query:    "CREATE TABLE test (pk int primary key)",
				Expected: []sql.Row{{types.NewOkResult(0)}},
			},
			{
				Query:    "SHOW TABLES",
				Expected: []sql.Row{{"test"}},
			},
		},
	},
	{
		Name: "CREATE DATABASE error handling",
		Assertions: []ScriptTestAssertion{
			{
				Query:       "create database `abc/def`",
				ExpectedErr: sql.ErrInvalidDatabaseName,
			},
			{
				Query:    "CREATE DATABASE newtestdb CHARACTER SET utf8mb4 ENCRYPTION='N'",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query:    "SHOW WARNINGS /* 1 */",
				Expected: []sql.Row{{"Warning", 1235, "Setting CHARACTER SET, COLLATION and ENCRYPTION are not supported yet"}},
			},
			{
				Query:    "CREATE DATABASE newtest1db DEFAULT COLLATE binary ENCRYPTION='Y'",
				Expected: []sql.Row{{types.NewOkResult(1)}},
			},
			{
				Query: "SHOW WARNINGS /* 2 */",
				Expected: []sql.Row{
					{"Warning", 1235, "Setting CHARACTER SET, COLLATION and ENCRYPTION are not supported yet"},
				},
			},
			{
				Query:       "CREATE DATABASE mydb",
				ExpectedErr: sql.ErrDatabaseExists,
			},
			{
				Query:    "CREATE DATABASE IF NOT EXISTS mydb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "SHOW WARNINGS /* 3 */",
				Expected: []sql.Row{{"Note", 1007, "Can't create database mydb; database exists "}},
			},
		},
	},
}

var DropDatabaseScripts = []ScriptTest{
	{
		Name: "DROP DATABASE correctly works",
		Assertions: []ScriptTestAssertion{
			{
				Query:    "DROP DATABASE mydb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{nil}},
			},
			{
				// TODO: incorrect error returned because the currentdb is not set to empty
				Skip:        true,
				Query:       "SHOW TABLES",
				ExpectedErr: sql.ErrNoDatabaseSelected,
			},
		},
	},
	{
		Name: "DROP DATABASE works on newly created databases.",
		SetUpScript: []string{
			"CREATE DATABASE testdb",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "USE testdb",
				Expected: []sql.Row{},
			},
			{
				Query:    "DROP DATABASE testdb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:       "USE testdb",
				ExpectedErr: sql.ErrDatabaseNotFound,
			},
		},
	},
	{
		Name: "DROP DATABASE works on current database and sets current database to empty.",
		SetUpScript: []string{
			"CREATE DATABASE testdb",
			"USE TESTdb",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "DROP DATABASE TESTDB",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{nil}},
			},
			{
				Query:       "USE testdb",
				ExpectedErr: sql.ErrDatabaseNotFound,
			},
		},
	},
	{
		Name: "DROP SCHEMA works on newly created databases.",
		SetUpScript: []string{
			"CREATE SCHEMA testdb",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "DROP SCHEMA TESTDB",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:       "USE testdb",
				ExpectedErr: sql.ErrDatabaseNotFound,
			},
		},
	},
	{
		Name: "DROP DATABASE IF EXISTS correctly works.",
		SetUpScript: []string{
			"DROP DATABASE mydb",
			"CREATE DATABASE testdb",
		},
		Assertions: []ScriptTestAssertion{
			{
				Query:    "DROP DATABASE IF EXISTS mydb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "SHOW WARNINGS",
				Expected: []sql.Row{{"Note", 1008, "Can't drop database mydb; database doesn't exist "}},
			},
			{
				Query:    "DROP DATABASE IF EXISTS testdb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
			},
			{
				Query:    "SHOW WARNINGS",
				Expected: []sql.Row{},
			},
			{
				Query:    "SELECT DATABASE()",
				Expected: []sql.Row{{nil}},
			},
			{
				Query:       "USE testdb",
				ExpectedErr: sql.ErrDatabaseNotFound,
			},
			{
				Query:    "DROP DATABASE IF EXISTS testdb",
				Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
			},
			{
				Query:    "SHOW WARNINGS",
				Expected: []sql.Row{{"Note", 1008, "Can't drop database testdb; database doesn't exist "}},
			},
		},
	},
}
